diff --git a/.gitignore b/.gitignore index ee44a96..0cdd3d9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -.idea -target +.idea +target diff --git a/build.sbt b/build.sbt deleted file mode 100644 index cd6b450..0000000 --- a/build.sbt +++ /dev/null @@ -1,54 +0,0 @@ -import sbt.Keys._ - -organization := "org.asem" - -name := "phinp" - -version := "1.0" - -scalaVersion := "2.11.8" - -scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8", "-feature") - -resolvers ++= Seq( - "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/", - "Spray repository" at "http://repo.spray.io/", - "Scalaz Bintray Repo" at "http://dl.bintray.com/scalaz/releases", - "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots", - "Central" at "http://central.maven.org/maven2" -) - -libraryDependencies ++= { - val akkaV = "2.+" - val sprayV = "1.+" - val orientV = "2.+" - - Seq( - "io.spray" %% "spray-can" % sprayV, - "io.spray" %% "spray-routing" % sprayV, - "io.spray" %% "spray-caching" % sprayV, - "io.spray" %% "spray-json" % "1.3.2", - - "com.typesafe.akka" %% "akka-actor" % akkaV, - "org.scala-lang.modules" % "scala-xml_2.11" % "1.0.5", - "org.scala-lang" % "scala-reflect" % "2.11.8", - - // Orient DB dependencies - "com.orientechnologies" % "orientdb-core" % orientV, - "com.orientechnologies" % "orientdb-client" % orientV, - "com.orientechnologies" % "orientdb-jdbc" % orientV, - "com.orientechnologies" % "orientdb-graphdb" % orientV, - "com.orientechnologies" % "orientdb-distributed" % orientV, - "com.tinkerpop.blueprints" % "blueprints-core" % "2.6.0", - "com.github.nscala-time" %% "nscala-time" % "2.+", - "com.fasterxml.jackson.datatype" % "jackson-datatype-joda" % "2.+", - "com.fasterxml.jackson.module" % "jackson-module-scala_2.11" % "2.+", - "com.wandoulabs.akka" %% "spray-websocket" % "0.1.4", - - "io.spray" %% "spray-testkit" % sprayV % "test", - "com.typesafe.akka" %% "akka-testkit" % akkaV % "test", - "org.scalatest" % "scalatest_2.11" % "3.0.0" % "test" - ) -} - -Revolver.settings \ No newline at end of file diff --git a/pom.xml b/pom.xml index 5964ecf..2be4a7b 100644 --- a/pom.xml +++ b/pom.xml @@ -2,48 +2,21 @@ 4.0.0 ru.sbt - phinp-mv + Sbira 1.0-SNAPSHOT jar UTF-8 1.8 - 1.3.3 - 2.2.8 - 2.4.9 + 2.2.12 + 2.4.11 - + - - io.spray - spray-can_2.11 - ${spray.version} - - - io.spray - spray-routing_2.11 - ${spray.version} - - - io.spray - spray-caching_2.11 - ${spray.version} - - - io.spray - spray-json_2.11 - 1.3.2 - - - io.spray - spray-util_2.11 - ${spray.version} - - - com.typesafe.akka akka-http-xml-experimental_2.11 ${akka.version} - - com.typesafe.akka - akka-sample-cluster-experimental_2.11.0-M3 - ${akka.version} - --> com.typesafe.akka akka-actor_2.11 ${akka.version} - - - com.fasterxml.jackson.core - jackson-databind - 2.8.1 - - - - - com.wandoulabs.akka - spray-websocket_2.11 - 0.1.4 - - - @@ -113,20 +49,6 @@ joda-time 2.9.4 - - - com.fasterxml.jackson.module - jackson-module-scala_2.11 - 2.8.0.rc2 - - - com.fasterxml.jackson.datatype - jackson-datatype-joda - 2.8.0.rc2 - - @@ -150,7 +72,38 @@ blueprints-core 2.6.0 - + + + com.fasterxml.jackson.core + jackson-databind + 2.8.4 + + + com.fasterxml.jackson.module + jackson-module-scala_2.11 + 2.8.3 + + + com.fasterxml.jackson.datatype + jackson-datatype-joda + 2.8.4 + + + + com.softwaremill.akka-http-session + core_2.11 + 0.2.7 + + + com.softwaremill.akka-http-session + jwt_2.11 + 0.2.7 + + @@ -165,25 +118,29 @@ junit 4.12 - - org.scalatest scalatest_2.11 - 3.0.0-SNAP6 + 3.0.0 test - io.spray - spray-testkit_2.11 - 1.3.3 + com.typesafe.akka + akka-testkit_2.11 + ${akka.version} + test + + + com.typesafe.akka + akka-http-testkit_2.11 + ${akka.version} test - + @@ -207,7 +164,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.7 + 2.19.1 true @@ -272,7 +229,7 @@ true - org.asem.Boot + ru.sbt.Main lib diff --git a/project/build.properties b/project/build.properties deleted file mode 100644 index 13d3ee7..0000000 --- a/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version = 0.13.12 \ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt deleted file mode 100644 index 1b3112a..0000000 --- a/project/plugins.sbt +++ /dev/null @@ -1,2 +0,0 @@ -logLevel := Level.Warn -addSbtPlugin("io.spray" % "sbt-revolver" % "0.8.0") \ No newline at end of file diff --git a/src/main/java/org/asem/orient/AGraph.java b/src/main/java/ru/sbt/orient/AGraph.java similarity index 94% rename from src/main/java/org/asem/orient/AGraph.java rename to src/main/java/ru/sbt/orient/AGraph.java index b7dd259..8f5a999 100644 --- a/src/main/java/org/asem/orient/AGraph.java +++ b/src/main/java/ru/sbt/orient/AGraph.java @@ -1,4 +1,4 @@ -package org.asem.orient; +package ru.sbt.orient; import com.tinkerpop.blueprints.impls.orient.OrientGraph; diff --git a/src/main/java/org/asem/orient/Query.java b/src/main/java/ru/sbt/orient/Query.java similarity index 98% rename from src/main/java/org/asem/orient/Query.java rename to src/main/java/ru/sbt/orient/Query.java index 37b16cc..029884d 100644 --- a/src/main/java/org/asem/orient/Query.java +++ b/src/main/java/ru/sbt/orient/Query.java @@ -1,4 +1,4 @@ -package org.asem.orient; +package ru.sbt.orient; import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery; import com.tinkerpop.blueprints.impls.orient.OrientDynaElementIterable; diff --git a/src/main/resources/address.sql b/src/main/resources/address.sql deleted file mode 100644 index 2da4856..0000000 --- a/src/main/resources/address.sql +++ /dev/null @@ -1,18 +0,0 @@ -create class CodeWithName; -create property CodeWithName.code string; -create property CodeWithName.name string; -alter property CodeWithName.code mandatory true; -alter property CodeWithName.name mandatory true; - -create class Address; -create property Address.city embedded CodeWithName; -create property Address.street embedded CodeWithName; -create property Address.building embedded CodeWithName; -alter property Address.city mandatory true; -alter property Address.street mandatory true; -alter property Address.building mandatory true; - -CREATE INDEX UxPharmacyAddress ON Pharmacy(address) UNIQUE; -insert into PrjCycle(name, visits, startDate, endDate) values ('Цикл-1', [{"@type" : "d", "@class": "VisitReq", "type":"P","num":20}], '2016-07-31', '2016-09-03') -insert into Pharmacy(name, cityName, streetName, buildingName) values ("Аптека 36.6", 'Москва', 'Новоданиловская', '10') -drugs- {"@type":"d","@class":"Drug","name":"Analgin","existence":true,"price":99.99} \ No newline at end of file diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 5adcfac..a522a50 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -1,28 +1,26 @@ akka { - loglevel = INFO + loglevel = DEBUG } -spray.can.server { - host = 0.0.0.0 - port = 8080 - request-timeout = 10s - ssl-encryption = off - - idle-timeout = 1800 s - request-chunk-aggregation-limit = 0 - - parsing.max-content-length = 5g - parsing.incoming-auto-chunking-threshold-size = 45k +akka { + http { + session { + encrypt-data = true +# max-age = 10 + } + } } -spray.websocket { - # Supported Per-message Compression Extensions. To disable, set to [] - #pmce = [] - pmce = ["permessage-deflate"] +http { + interface = "0.0.0.0" + port = 9080 + sslPort = 9443 } -spray.routing { - file-chunking-threshold-size = 512k +database { + url: "remote:localhost/probe" + user: root + password: 123456 } secureKey { @@ -32,16 +30,11 @@ secureKey { keyPassword: "123456" } +cors.allowed-origin = "*" + mainPage = "/pub/app/simple.html" loginPage = "/pub/login.html" -database { - url: "remote:localhost/probe" - user: root - password: 123456 -} - - #akka { # actor { # provider = "akka.cluster.ClusterActorRefProvider" diff --git a/src/main/resources/schema.osql b/src/main/resources/schema.osql deleted file mode 100644 index 814c51d..0000000 --- a/src/main/resources/schema.osql +++ /dev/null @@ -1,167 +0,0 @@ -connect remote:localhost/pharma root 123456; - -DROP CLASS PhUser; -DROP CLASS Project; -DROP CLASS VisitReq; -DROP CLASS PrjCycle; -DROP CLASS PharmNet; -DROP CLASS CodeWithName; -DROP CLASS Address; -DROP CLASS Pharmacy; -DROP CLASS Manager; -DROP CLASS Employee; -DROP CLASS Owner; -DROP CLASS Worker; -DROP CLASS Report; -DROP CLASS Drug; -DROP CLASS PhNet; -DROP CLASS ReportWorker; -DROP CLASS ReportChecker; -DROP CLASS ReportCycle; -DROP CLASS ReportPharm; -DROP CLASS PharmCycle; -DROP CLASS PrjCycleWorker; -DROP CLASS Task; -DROP CLASS Comment; - -CREATE CLASS PhUser EXTENDS V; -CREATE PROPERTY PhUser.login STRING; -CREATE PROPERTY PhUser.password STRING; -CREATE PROPERTY PhUser.email STRING; -CREATE PROPERTY PhUser.firstName STRING; -CREATE PROPERTY PhUser.secondName STRING; -CREATE PROPERTY PhUser.activated boolean; - -alter property PhUser.login COLLATE ci; -alter property PhUser.email COLLATE ci; -ALTER PROPERTY PhUser.login MANDATORY TRUE; -ALTER PROPERTY PhUser.password MANDATORY TRUE; -ALTER PROPERTY PhUser.email MANDATORY TRUE; -ALTER PROPERTY PhUser.activated MANDATORY TRUE; -ALTER PROPERTY PhUser.activated DEFAULT 0; - -CREATE INDEX UxPhUserLogin ON PhUser(login) UNIQUE STRING; -CREATE INDEX UxPhUserEmail ON PhUser(email) UNIQUE STRING; - -CREATE CLASS Project EXTENDS V; -CREATE PROPERTY Project.name STRING; -CREATE PROPERTY Project.startDate DATE; -ALTER PROPERTY Project.name MANDATORY TRUE; -ALTER PROPERTY Project.startDate MANDATORY TRUE; -CREATE INDEX UxProjectName ON Project(name) UNIQUE STRING; - -create class VisitReq; -create property VisitReq.num short; -create property VisitReq.type string ; -ALTER PROPERTY VisitReq.type regexp "[PH|DR]"; -ALTER PROPERTY VisitReq.num MANDATORY TRUE; -ALTER PROPERTY VisitReq.type MANDATORY TRUE; - -create class Drug; -create property Drug.name String; -create property Drug.existence Boolean; -create property Drug.price Decimal; - -create class PrjCycle extends v; -create property PrjCycle.name string; -create property PrjCycle.startDate date; -create property PrjCycle.endDate date; -create property PrjCycle.visits embeddedlist VisitReq; -create property PrjCycle.drugs embeddedlist Drug; - -ALTER PROPERTY PrjCycle.name MANDATORY TRUE; -ALTER PROPERTY PrjCycle.startDate MANDATORY TRUE; -ALTER PROPERTY PrjCycle.endDate MANDATORY TRUE; -ALTER PROPERTY PrjCycle.visits MANDATORY TRUE; - -CREATE CLASS PrjCycleWorker EXTENDS E; -CREATE PROPERTY PrjCycleWorker.in LINK PhUser; -CREATE PROPERTY PrjCycleWorker.out LINK PrjCycle; -CREATE INDEX UxPrjCycleWorker ON PrjCycleWorker(out,in) UNIQUE; - -CREATE CLASS PharmNet EXTENDS V; -CREATE PROPERTY PharmNet.name STRING; -CREATE PROPERTY PharmNet.contract STRING; -ALTER PROPERTY PharmNet.name MANDATORY TRUE; -CREATE INDEX UxPharmNet ON PharmNet(name) UNIQUE; - -CREATE CLASS Pharmacy EXTENDS V; -CREATE PROPERTY Pharmacy.name STRING; -CREATE PROPERTY Pharmacy.chiefPhone STRING; -CREATE PROPERTY Pharmacy.chiefName STRING; -CREATE PROPERTY Pharmacy.tradeRoomPhone STRING; -create property Pharmacy.cityName String; -create property Pharmacy.streetName String; -create property Pharmacy.buildingName String; -create property Pharmacy.cityCode String; -create property Pharmacy.streetCode String; -create property Pharmacy.buildingCode String; - -ALTER PROPERTY Pharmacy.name MANDATORY TRUE; -alter property Pharmacy.cityName mandatory true; -alter property Pharmacy.streetName mandatory true; -alter property Pharmacy.buildingName mandatory true; - -CREATE INDEX UxPharmacyAddress ON Pharmacy(cityName, streetName, buildingName) UNIQUE; - -CREATE CLASS PharmCycle EXTENDS E; -CREATE PROPERTY PharmCycle.out LINK PrjCycle; -CREATE PROPERTY PharmCycle.in LINK Pharmacy; -ALTER PROPERTY PharmCycle.out MANDATORY TRUE; -ALTER PROPERTY PharmCycle.in MANDATORY TRUE; -CREATE INDEX UxPharmCycle ON PharmCycle(out,in) UNIQUE; - -CREATE CLASS PhNet EXTENDS E; -CREATE PROPERTY PhNet.out LINK Pharmacy; -CREATE PROPERTY PhNet.in LINK PharmNet; -ALTER PROPERTY PhNet.out MANDATORY TRUE; -ALTER PROPERTY PhNet.in MANDATORY TRUE; -CREATE INDEX UxPhNet ON PhNet(out,in) UNIQUE; - -CREATE CLASS Worker EXTENDS E; -CREATE PROPERTY Worker.out LINK PhUser; -CREATE PROPERTY Worker.in LINK Project; -ALTER PROPERTY Worker.out MANDATORY TRUE; -ALTER PROPERTY Worker.in MANDATORY TRUE; -CREATE INDEX UxWorker ON Worker(out,in) UNIQUE; - -CREATE CLASS Manager EXTENDS Worker; -CREATE CLASS Employee EXTENDS Worker; - -CREATE CLASS Report EXTENDS V; -create property Report.createDate Date; -create property Report.drugs embeddedlist Drug; - -CREATE CLASS ReportWorker EXTENDS E; -CREATE PROPERTY ReportWorker.in LINK PhUser; -CREATE PROPERTY ReportWorker.out LINK Report; -CREATE INDEX UxReportWorker ON ReportWorker(out,in) UNIQUE; - -CREATE CLASS ReportChecker EXTENDS E; -CREATE PROPERTY ReportChecker.in LINK PhUser; -CREATE PROPERTY ReportChecker.out LINK Report; -CREATE INDEX UxReportChecker ON ReportChecker(out,in) UNIQUE; - -CREATE CLASS ReportCycle EXTENDS E; -CREATE PROPERTY ReportCycle.in LINK PrjCycle; -CREATE PROPERTY ReportCycle.out LINK Report; -CREATE INDEX UxReportCycle ON ReportCycle(out,in) UNIQUE; - -CREATE CLASS ReportPharm EXTENDS E; -CREATE PROPERTY ReportPharm.in LINK Pharmacy; -CREATE PROPERTY ReportPharm.out LINK Report; -CREATE INDEX UxReportPharm ON ReportPharm(out,in) UNIQUE; - -CREATE CLASS Comment EXTENDS V; -CREATE PROPERTY Comment.owner String; -CREATE PROPERTY Comment.comment String; -CREATE PROPERTY Comment.createDate Date; - -CREATE CLASS Task EXTENDS V; -CREATE PROPERTY Task.name String; -CREATE PROPERTY Task.content String; -CREATE PROPERTY Task.status String; -CREATE PROPERTY Task.assignedPerson String; -CREATE PROPERTY Task.changeDate DateTime; -CREATE PROPERTY Task.deadLine DateTime; -CREATE PROPERTY Task.owner String; diff --git a/src/main/scala/org/asem/Boot.scala b/src/main/scala/org/asem/Boot.scala deleted file mode 100644 index 6bf630e..0000000 --- a/src/main/scala/org/asem/Boot.scala +++ /dev/null @@ -1,76 +0,0 @@ -package org.asem - -import java.security.{KeyStore, SecureRandom} -import javax.net.ssl.{KeyManagerFactory, SSLContext, TrustManagerFactory} - -import _root_.spray.can.Http -import _root_.spray.can.server.UHttp -import _root_.spray.io.ServerSSLEngineProvider -import akka.actor.{Actor, ActorLogging, ActorSystem, Props} -import akka.io.IO -import akka.pattern.ask -import akka.util.Timeout -import com.typesafe.config.ConfigFactory -import org.asem.orient.services._ -import org.asem.spray.security.RSA - -import scala.concurrent.duration._ -import scala.language.postfixOps - -/** - * Created by gosha-user on 16.07.2016. - * @see http://spray.io/wjax - */ -object Boot extends App { - final case class PushToChildren(msg: String) - - object WebSocketServer { - def props() = Props(classOf[WebSocketServer]) - } - - class WebSocketServer extends Actor with ActorLogging { - def receive = { - // when a new connection comes in we register a WebSocketConnection actor as the per connection handler - case Http.Connected(remoteAddress, localAddress) => - val serverConnection = sender() - val conn = context.actorOf(Props(classOf[MainServiceActor], serverConnection)) - serverConnection ! Http.Register(conn) - - case PushToChildren(msg: String) => - val children = context.children - children.foreach(ref => ref ! Push(msg)) - } - } - - implicit val system = ActorSystem("spray-server") - - val service = system.actorOf(Props[WebSocketServer], "sber-spray-service") // for websocket - val config = ConfigFactory.load() - - implicit val mySSLContext: SSLContext = { - val context = SSLContext.getInstance("TLS") - - val keyStore: KeyStore = RSA.keyStore - val keyManagerFactory: KeyManagerFactory = KeyManagerFactory.getInstance("SunX509"); - keyManagerFactory.init(keyStore, RSA.getPassword.getPassword); - - val trustManagerFactory: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509"); - trustManagerFactory.init(keyStore); - - context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom()); - context - } - - implicit val myEngineProvider = ServerSSLEngineProvider { engine => - engine.setEnabledProtocols(Array("TLSv1.2")) - engine - } - - implicit val timeout = Timeout(5 seconds) - IO(UHttp) ? Http.Bind(service, interface = config.getString("spray.can.server.host"), port = config.getInt("spray.can.server.port")) - -// while (true) { -// var msg = readLine("Give a message and hit ENTER to push message to the children ...\n") -// service ! PushToChildren(msg) -// } -} \ No newline at end of file diff --git a/src/main/scala/org/asem/orient/model/BaseModelFuncs.scala b/src/main/scala/org/asem/orient/model/BaseModelFuncs.scala deleted file mode 100644 index af02ecc..0000000 --- a/src/main/scala/org/asem/orient/model/BaseModelFuncs.scala +++ /dev/null @@ -1,172 +0,0 @@ -package org.asem.orient.model - -import java.util.Date - -import com.tinkerpop.blueprints.Vertex -import org.joda.time.DateTime -import org.joda.time.format.ISODateTimeFormat -import spray.json._ - -import scala.language.{implicitConversions, postfixOps} -import scala.reflect.ClassTag -import scala.reflect.runtime._ -import scala.reflect.runtime.universe._ - -/** - * Created by gosha-user on 20.08.2016. - */ -trait BaseModelFuncs { - val dateFormatter = ISODateTimeFormat.basicDate() - - /** - * Functon generate map (field -> value) from all object fieds with public accessors - * - * @param rep report object to generate map - * @return generated map object - */ - def entity2Map[T : ClassTag](rep: T) = { - import scala.reflect.runtime.universe._ - - val rm = scala.reflect.runtime.currentMirror - val accessors = rm.classSymbol(rep.getClass).toType.members.collect { - case m: MethodSymbol if m.isGetter && m.isPublic => m - } - - val instanceMirror = rm.reflect(rep) - (for (acc <- accessors) yield acc.name.toString -> instanceMirror.reflectMethod(acc).apply()) toMap - } - - /** - * Function generate Json map from Report fields using scala reflection mechanizm - * @param rep report objects - * @return generated json map - */ - implicit def entity2JsonMap[T : ClassTag](rep: T):Map[String, JsValue] = { - val rm = scala.reflect.runtime.currentMirror - val accessors = rm.classSymbol(rep.getClass).toType.members.collect { - case m: MethodSymbol if m.isGetter && m.isPublic => m - } - - val instanceMirror = rm.reflect(rep) - val seq = for { - acc <- accessors - value = instanceMirror.reflectMethod(acc).apply() - if value != null - } yield { - acc.name.toString -> ( - value match { - case s:String => JsString(s) - case d:DateTime => JsString(dateFormatter.print(d)) - case i:Int => JsNumber(i) - case b:Boolean => JsBoolean(b) - case Some(b:Boolean) => JsBoolean(b) - case None => JsNull - }) - } - - seq.toMap - } - - implicit def date2datetime (date:Date):DateTime = { - new DateTime(date) - } - - implicit def datetime2date (datetime: DateTime): Date = { - datetime.toDate - } - - implicit def js2string(x: Option[JsValue]):String = { - x match { - case Some(JsString(s)) => s - case _ => null - } - } - - implicit def js2date(x: Option[JsValue]):DateTime = { - x match { - case Some(JsString(s)) => dateFormatter.parseDateTime(s) - case _ => null - } - } - - implicit def js2int(x: Option[JsValue]):BigInt = { - x match { - case Some(JsNumber(n)) => n.toBigInt - case _ => null - } - } - - implicit def js2double(x: Option[JsValue]):BigDecimal = { - x match { - case Some(JsNumber(n)) => n - case _ => null - } - } - - implicit def js2boolean (x: Option[JsValue]):Boolean = { - x match { - case Some(JsBoolean(b)) => b - case _ => false - } - } - - implicit def js2booleanOpt (x: Option[JsValue]):Option[Boolean] = { - x match { - case Some(JsBoolean(b)) => Some(b) - case _ => None - } - } - - def createFromVertex[T : TypeTag](vtx: Vertex)(ctor: Int = 0): T = { - val tt = typeTag[T] - val ctr:MethodMirror = currentMirror.reflectClass(tt.tpe.typeSymbol.asClass).reflectConstructor ( - tt.tpe.members.filter(m => m.isMethod && m.asMethod.isConstructor).iterator.toSeq(ctor).asMethod - ) - - val args = for { - param <- ctr.symbol.paramLists.head - } - yield { - if (param.name.toString == "id") - vtx.getId.toString.replace("#", "") - else if (param.name.toString == "password") - "" - else param.typeSignature.toString match { - case "String" => vtx.getProperty[String](param.name.toString) - case "org.joda.time.DateTime" => val d: DateTime = vtx.getProperty[Date](param.name.toString); d - case "BingInt" => vtx.getProperty[Int](param.name.toString) - case "BigDecimal" => vtx.getProperty[Double](param.name.toString) - case "Boolean" => vtx.getProperty[Boolean](param.name.toString) - case "Option[Boolean]" => if (vtx.getPropertyKeys.contains(param.name.toString)) Some(vtx.getProperty[Boolean](param.name.toString)) else None - case _ => vtx.getProperty[String](param.name.toString) - } - } - - ctr(args: _*).asInstanceOf[T] - } - - def createFromJson[T : TypeTag](obj: JsObject)(ctor: Int = 0): T = { - val tt = typeTag[T] - val ctr:MethodMirror = currentMirror.reflectClass(tt.tpe.typeSymbol.asClass).reflectConstructor ( - tt.tpe.members.filter(m => m.isMethod && m.asMethod.isConstructor).iterator.toSeq(ctor).asMethod - ) - - val fields = obj.fields - val args = for { - param <- ctr.symbol.paramLists.head - } - yield { - param.typeSignature.toString match { - case "String" => val s:String = fields.get(param.name.toString); s - case "org.joda.time.DateTime" => val d:DateTime = fields.get(param.name.toString); d - case "Int" => val i:BigInt = fields.get(param.name.toString); i - case "Double" => val db:BigDecimal = fields.get(param.name.toString); db - case "Boolean" => val b:Boolean = fields.get(param.name.toString); b - case "Option[Boolean]" => val b:Option[Boolean] = fields.get(param.name.toString); b - case _ => val s:String = fields.get(param.name.toString); s - } - } - - ctr(args: _*).asInstanceOf[T] - } -} \ No newline at end of file diff --git a/src/main/scala/org/asem/orient/model/Operation.scala b/src/main/scala/org/asem/orient/model/Operation.scala deleted file mode 100644 index 5fcbf02..0000000 --- a/src/main/scala/org/asem/orient/model/Operation.scala +++ /dev/null @@ -1,8 +0,0 @@ -package org.asem.orient.model - -import org.asem.orient.model.entities.BaseEntity - -/** - * Created by gosha-user on 03.09.2016. - */ -final case class Operation (message:String, success:Boolean = false) extends BaseEntity diff --git a/src/main/scala/org/asem/orient/model/PhUser.scala b/src/main/scala/org/asem/orient/model/PhUser.scala deleted file mode 100644 index a8ec06d..0000000 --- a/src/main/scala/org/asem/orient/model/PhUser.scala +++ /dev/null @@ -1,71 +0,0 @@ -package org.asem.orient.model - -import java.security._ -import java.util._ - -import com.tinkerpop.blueprints.impls.orient.OrientVertex -import spray.json._ - -import scala.language.implicitConversions - -/** - * Class represents user for pharmacy input system - * Created by gosha-user on 30.07.2016. - */ -case class PhUser(login: String, password: String, email: String = "", firstName: String = "", secondName: String = "", activated: Option[Boolean] = None, manager: Option[Boolean] = None, id:String = "") { - require(login != null, "login should be provided") -// require(password != null, "password should be provided") -// require(email != null, "email should be provided") - - lazy val pwdHash = computeHash(password) - - override def toString = login + "," + email + "," + firstName + "," + secondName + "," + manager.getOrElse(false) + "," + id - - def computeHash(pwd: String): String = { - if (pwd == null || pwd.isEmpty) { - pwd - } - else { - val dig = MessageDigest.getInstance("SHA1").digest(pwd.getBytes) - Base64.getEncoder.encodeToString(dig) - } - } - - def unapply(user:PhUser) = Some (user.login, user.pwdHash, user.email, user.firstName, user.secondName, user.activated, user.manager, user.id) -} - -/** - * Object used to deserialize PhUser from string, using scala regexp unapply function - */ -object PhUser extends DefaultJsonProtocol with BaseModelFuncs { - private val PhUserRegex = "(.*),(.*),(.*),(.*),(.*),(.*)".r - - def fromString(str: String): Option[PhUser] = str match { - case PhUserRegex(login, email, firstName, secondName, manager, id) => Some(PhUser( - login = login, - password = "", - email = email, - firstName = firstName, - secondName = secondName, - activated = Some(true), - id = id, - manager = Some(manager match { - case "null" => false - case s:String => s.toBoolean - case _ => false - }))) - case _ => None - } - - def unapply(vtx: OrientVertex): Option[PhUser] = Some(createFromVertex[PhUser](vtx)(0)) - def unapply(obj: JsObject): Option[PhUser] = Some (createFromJson[PhUser](obj)(0)) - - implicit object PhUserJsonFormat extends RootJsonFormat[PhUser] { - override def write(obj: PhUser): JsValue = JsObject(fields = obj) - override def read(json: JsValue): PhUser = createFromJson[PhUser](json.asJsObject)(0) - } -} - - - - diff --git a/src/main/scala/org/asem/orient/model/entities/EdgeNames.scala b/src/main/scala/org/asem/orient/model/entities/EdgeNames.scala deleted file mode 100644 index 9212134..0000000 --- a/src/main/scala/org/asem/orient/model/entities/EdgeNames.scala +++ /dev/null @@ -1,16 +0,0 @@ -package org.asem.orient.model.entities - -object EdgeNames { - val PhNet = "PhNet" - val Worker = "Worker" - val Owner = "Owner" - val Employee = "Employee" - val Manager = "Manager" - val ReportWorker = "ReportWorker" - val ReportChecker = "ReportChecker" - val ReportCycle = "ReportCycle" - val ReportPharm = "ReportPharm" - val PharmCycle = "PharmCycle" - val PrjCycleWorker = "PrjCycleWorker" - val TaskWorker = "TaskWorker" -} diff --git a/src/main/scala/org/asem/orient/model/entities/JacksonJsonSupport.scala b/src/main/scala/org/asem/orient/model/entities/JacksonJsonSupport.scala deleted file mode 100644 index bd4ef58..0000000 --- a/src/main/scala/org/asem/orient/model/entities/JacksonJsonSupport.scala +++ /dev/null @@ -1,35 +0,0 @@ -package org.asem.orient.model.entities - -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.datatype.joda.JodaModule -import com.fasterxml.jackson.module.scala.DefaultScalaModule -import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper -import spray.http._ -import spray.httpx.marshalling.Marshaller -import spray.httpx.unmarshalling._ - -/** - * Created by gosha-user on 27.08.2016. - */ -trait JacksonJsonSupport { - import com.fasterxml.jackson.annotation.JsonInclude._ - protected val jacksonModules = Seq(DefaultScalaModule) - - protected val mapper = new ObjectMapper() with ScalaObjectMapper - - mapper.setSerializationInclusion(Include.NON_EMPTY) - mapper.registerModules(jacksonModules:_*) - mapper.registerModule(new JodaModule()); - - protected implicit def jacksonJsonUnmarshaller[T : Manifest] = - Unmarshaller[T](MediaTypes.`application/json`) { - case x: HttpEntity.NonEmpty => - val jsonSource = x.asString(defaultCharset = HttpCharsets.`UTF-8`) - mapper.readValue[T](jsonSource) - } - - protected implicit def jacksonJsonMarshaller[T <: BaseEntity] = Marshaller.delegate[T, String](ContentTypes.`application/json`)(mapper.writeValueAsString(_)) - protected implicit def jacksonJsonMarshaller1[T <: List[BaseEntity]] = Marshaller.delegate[T, String](ContentTypes.`application/json`)(mapper.writeValueAsString(_)) -// protected implicit def jacksonJsonMarshaller3[T <: Report] = Marshaller.delegate[T, String](ContentTypes.`application/json`)(mapper.writeValueAsString(_)) -// protected implicit def jacksonJsonMarshaller43[T <: List[Report]] = Marshaller.delegate[T, String](ContentTypes.`application/json`)(mapper.writeValueAsString(_)) -} diff --git a/src/main/scala/org/asem/orient/model/entities/JsonMapper.scala b/src/main/scala/org/asem/orient/model/entities/JsonMapper.scala deleted file mode 100644 index 3d1da67..0000000 --- a/src/main/scala/org/asem/orient/model/entities/JsonMapper.scala +++ /dev/null @@ -1,45 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package org.asem.orient.model.entities - -trait JsonMapper[B] { - import com.fasterxml.jackson.databind._ - import com.fasterxml.jackson.datatype.joda._ - import com.fasterxml.jackson.module.scala._ - import com.fasterxml.jackson.module.scala.experimental._ - - val mapper = new ObjectMapper() with ScalaObjectMapper - mapper.registerModule(DefaultScalaModule) - mapper.registerModule(new JodaModule()); - - def read(src: String)(implicit m: Manifest[B]):B = { - mapper.readValue[B](src) - } - - def write(rep:B):String = { - mapper.writeValueAsString(rep) - } -} - -object JsonMapper { - import com.fasterxml.jackson.databind._ - import com.fasterxml.jackson.datatype.joda._ - import com.fasterxml.jackson.module.scala._ - import com.fasterxml.jackson.module.scala.experimental._ - - val mapper = new ObjectMapper() with ScalaObjectMapper - mapper.registerModule(DefaultScalaModule) - mapper.registerModule(new JodaModule()); - - def read[B](src: String)(implicit m: Manifest[B]):B = { - mapper.readValue[B](src) - } - - def write[B](rep:B):String = { - mapper.writeValueAsString(rep) - } -} diff --git a/src/main/scala/org/asem/orient/model/entities/package.scala b/src/main/scala/org/asem/orient/model/entities/package.scala deleted file mode 100644 index 5c09754..0000000 --- a/src/main/scala/org/asem/orient/model/entities/package.scala +++ /dev/null @@ -1,518 +0,0 @@ -package org.asem.orient.model - -import com.orientechnologies.orient.core.sql.OCommandSQL -import java.util.Collections - -/** - * Created by gosha-user on 28.08.2016. - */ -package object entities { - import java.util.Date - - import com.orientechnologies.orient.core.db.record.OTrackedList - import com.orientechnologies.orient.core.id.ORecordId - import com.orientechnologies.orient.core.record.impl.ODocument - import com.tinkerpop.blueprints.Direction._ - import com.tinkerpop.blueprints.Vertex - import com.tinkerpop.blueprints.impls.orient.{OrientGraph, OrientVertex} - import org.asem.orient.Query - import org.joda.time.DateTime - - import scala.collection.JavaConversions._ - import scala.language.implicitConversions - - object DateTimeHelpers { - implicit def date2datetime(date: Date): DateTime = new DateTime(date) - implicit def datetime2date(datetime: DateTime): Date = datetime.toDate - } - - import DateTimeHelpers._ - - object VertexHelpers { - implicit class VertexHelper[B <: Vertex](val vtx:B) { - val id:String = vtx.getId.toString.replace("#", "") - def prop[B](name: String) = vtx.getProperty[B](name) - def field[T](name: String)(implicit m:Manifest[T]):T = vtx.asInstanceOf[OrientVertex].getRecord[ODocument].field(name, m.runtimeClass) - def asDoc = vtx.asInstanceOf[OrientVertex].getRecord[ODocument] - def out(label: String):Vertex = { - val vtxs = vtx.getVertices(OUT, label) - if (vtxs.iterator().hasNext) - vtxs.iterator().next() - else - null - } - - def outF(label: String, field:String) = { - val vtxs = vtx.getVertices(OUT, label) - if (vtxs.iterator().hasNext) - vtxs.head.prop[String](field) - else - "" - } - - def in(label: String):Vertex = { - val vtxs = vtx.getVertices(IN, label) - if (vtxs.iterator().hasNext) - vtxs.iterator().next() - else - null - } - - def out(label: String*):Iterable[Vertex] = vtx.getVertices(OUT, label:_*) - def in(label: String*):Iterable[Vertex] = vtx.getVertices(IN, label:_*) - } - } - - import VertexHelpers._ - - trait BaseEntity - - case class Comment(id:String = null, owner: String, comment: String, createDate: DateTime) extends BaseEntity - object Comment extends JsonMapper[Comment] { - def unapply(comment: String): Option[Comment] = Some(read(comment)) - def unapply[B <: Vertex](vtx: B): Option[Comment] = if (vtx != null) Some(Comment(id = vtx.id, owner = vtx.prop("owner"), comment = vtx.prop("comment"), createDate = vtx.prop[Date]("createDate"))) else None - } - - case class Task(id: String, name: String, content: String, comments: Seq[Comment], status: String, assignedPerson: String, changeDate: DateTime, deadLine: DateTime, owner: String) extends BaseEntity - object Task extends JsonMapper[Task] { - def unapply(comment: String): Option[Task] = Some(read(comment)) - def unapply[B <: Vertex](vtx: B): Option[Task] = if (vtx != null) Some( - Task( - id = vtx.id, - name = vtx.prop("name"), - content = vtx.prop("content"), - comments = {for {com:Vertex <- vtx.out()} yield {val Comment(c) = com; c}}.toList, - status = vtx.prop("status"), - assignedPerson = vtx.prop("assignedPerson"), - changeDate = vtx.prop[Date]("changeDate"), - deadLine = vtx.prop[Date]("deadLine"), - owner = vtx.prop("owner") - )) - else None - } - - case class PrjUser(id: String = null, login: String, firstName: String = "", secondName: String = "", email: String = "") extends BaseEntity - object PrjUser extends JsonMapper[PrjUser] { - def unapply(user: PhUser): Option[PrjUser] = Some(PrjUser(user.id, user.login, user.firstName, user.secondName, user.email)) - def unapply(user: String): Option[PrjUser] = Some(read(user)) - def unapply[B <: Vertex](vtx: B): Option[PrjUser] = if (vtx != null) Some(PrjUser(id = vtx.id, login = vtx.prop("login"))) else None - } - - case class Project(id: String = null, name: String, startDate: DateTime) extends BaseEntity - object Project extends JsonMapper[Project] { - def unapply(project: String): Option[Project] = Some(read(project)) - def unapply[B <: Vertex](vtx: B): Option[Project] = if (vtx != null) Some( - Project(id = vtx.id, name = vtx.prop("name"), startDate = vtx.prop[Date]("startDate")) - ) else None - } - - case class VisitReq(typeName: String, num: BigInt) - case class PrjCycle(id: String = null, name: String, startDate: DateTime, endDate: DateTime, visits: Iterable[VisitReq]) extends BaseEntity - object PrjCycle extends JsonMapper[PrjCycle] { - def unapply(prjCycle: String): Option[PrjCycle] = Some(read(prjCycle)) - def unapply[B <: Vertex](vtx: B): Option[PrjCycle] = if (vtx == null) None else { - val visits:OTrackedList[ODocument] = vtx.field("visits") - Some( - PrjCycle( - id = vtx.id, name = vtx.prop("name"), startDate = vtx.prop[Date]("startDate"), endDate = vtx.prop[Date]("endDate"), - for {visitRec <- visits} yield { - VisitReq(visitRec.field("type", classOf[String]), visitRec.field[Int]("num", classOf[Integer])) - } - ) - ) - } - } - - case class PharmNet(id: String = null, name: String, contract: String) extends BaseEntity - object PharmNet extends JsonMapper[PharmNet] { - def unapply[B <: Vertex](vtx: B): Option[PharmNet] = if (vtx != null) Some (PharmNet (id = vtx.id, name = vtx.prop("name"), contract = vtx.prop("contract"))) else None - } - - case class Pharmacy(id: String = null, name: String, chiefPhone: String, chiefName: String, tradeRoomPhone: String, - pharmNet: PharmNet, cityCode: String, cityName: String, streetCode: String, streetName: String, buildingCode: String, - buildingName: String, contractExt: String = "") extends BaseEntity - object Pharmacy extends JsonMapper[Pharmacy] { - def unapply[B <: Vertex](vtx: B): Option[Pharmacy] = if (vtx != null) - Some ( - Pharmacy ( - id = vtx.id, - name = vtx.prop("name"), - chiefPhone = vtx.prop("chiefPhone"), - chiefName = vtx.prop("chiefName"), - tradeRoomPhone = vtx.prop("tradeRoomPhone"), - pharmNet = PharmNet.unapply(vtx.out("PhNet")).getOrElse(null), - cityCode = vtx.prop("cityCode"), - cityName = vtx.prop("cityName"), - streetCode = vtx.prop("streetCode"), - streetName = vtx.prop("streetName"), - buildingCode = vtx.prop("buildingCode"), - buildingName = vtx.prop("buildingName"), - contractExt = vtx.prop("contractExt") - ) - ) - else None - } - - case class Drug(name: String, existence:Boolean, price: BigDecimal) - case class Report(id: String = null, createDate:DateTime, cycle: PrjCycle, owner: PrjUser, pharmacy: Pharmacy, drugs: Iterable[Drug], checked: Boolean = false, checker: PrjUser) extends BaseEntity - object Report extends JsonMapper[Report] { - import java.math.{BigDecimal => JavaDecimal} - def unapply(project: String): Option[Report] = Some(read(project)) - def unapply[B <: Vertex](vtx: B): Option[Report] = { - val rep = Report( - id = vtx.id, - createDate = vtx.prop[Date]("createDate"), - cycle = PrjCycle.unapply(vtx.out("ReportCycle")).getOrElse(null), - owner = PrjUser.unapply(vtx.in("ReportWorker")).getOrElse(null), - pharmacy = Pharmacy.unapply(vtx.out("ReportPharm")).getOrElse(null), - drugs = null, - checker = PrjUser.unapply(vtx.out("ReportChecker")).getOrElse(null) - ) - - val drugsList: OTrackedList[ODocument] = vtx.field("drugs") - if (drugsList != null) { - val drugs = for {drugRec <- drugsList} yield { - Drug( - name = drugRec.field("name", classOf[String]), - existence = drugRec.field("existence", classOf[Boolean]), - price = drugRec.field[JavaDecimal]("price", classOf[JavaDecimal]) - ) - } - - Some(rep.copy(drugs = drugs)) - } - else - Some (rep) - } - } - - object PrjService { - import java.util.{Map => JavaMap, List => JavaList} - val idPattern = "([0-9]+:[0-9]+)".r - - /** - * Function used to check format of identifier - * @param id identifier to check - * @return identifier if suitable or throw an error - */ - private def checkId (id:String):String = id match { - case idPattern(c) => c - case _ => throw new IllegalArgumentException ("Wrong id is provided: " + id) - } - - private def findVertexByAttrs(tx: OrientGraph, clazz: String, attrNames:Array[String], attrValues:Array[Object]): Option[Vertex] = { - val vtxs = tx.getVertices(clazz, attrNames, attrValues) - if (vtxs.iterator.hasNext) { - Some(vtxs.iterator.next.asInstanceOf[Vertex]) - } - else - None - } - - /** - * Fucntin retrieves all the projects for given user - * @param userId user identifier - * @return list of projects - */ - def findAllProjects (userId:String):OrientGraph => List[Project] = tx => tx.getVertex("#" + userId) match { - case vtx: Vertex => {for (vtxPrj <- vtx.getVertices(OUT, "Worker", "Employee", "Manager")) yield {val Project(prj) = vtxPrj; prj}}.toList - case _ => null - } - - /** - * Fucntion retrieves all project cycles for given user and project. Project cycles also filtered by date (current date) - * @param prj project - * @param user user - * @return active project cycles - */ - def findActivePrjCycles(prjId:String, userId:String):OrientGraph => List[PrjCycle] = { - val sqlQuery = """select * - |from PrjCycle - |where in() in [:projectId, :userId] and SYSDATE() between startDate and endDate - |order by startDate""".stripMargin.replaceAll("\n", " ") - val params = Map("projectId" -> new ORecordId("#" + prjId), "userId" -> new ORecordId("#" + userId)) - tx => { - val vtxs = Query.executeQuery(tx, sqlQuery, params) - for (vtxPrj <- vtxs) yield { val PrjCycle (prj) = vtxPrj; prj} - }.toList - } - - /** - * Function retrieves proj cycle by projcycle id - * @param id identifier of project cycle - * @return project cycle - */ - def findPrjCycleById (id: String): OrientGraph => PrjCycle = tx => {val PrjCycle(pc) = tx.getVertex("#" + id); pc} - - /** - * Function retirves all reports for given project cycle - * @param cycleId project cycle - * @return list of reports for this cycle w/o any filtering - */ - def findAllReports (cycleId:String): OrientGraph => List[Report] = tx => { - val sqlQuery = s"select expand(in('${EdgeNames.ReportCycle}')) from #" + checkId(cycleId) - val vtxs = Query.executeQuery(tx, sqlQuery, Collections.EMPTY_MAP) - println ("vtxs: " + vtxs + ", " + sqlQuery) - for (vtxRep <- vtxs) yield {val Report (rep) = vtxRep; rep} - }.toList - - /** - * Funсtion retrieves reports for given project cycle and user (owner of the report) - * @param cycleId project cycle - * @param userId user - * @return list of reports - */ - def findReportsForUser (cycleId:String, userId:String): OrientGraph => List[Report] = tx => { - val SQL_QUERY = s"select expand(in('${EdgeNames.ReportCycle}')) from :cycleId where in('${EdgeNames.ReportCycle}').out('${EdgeNames.ReportWorker}') in [:userId]" - val params = Map("cycleId" -> new ORecordId("#" + checkId(cycleId)), "userId" -> new ORecordId("#" + checkId(userId))) - val vtxs = Query.executeQuery(tx, SQL_QUERY, params) - for (vtxRep <- vtxs) yield {val Report (rep) = vtxRep; rep} - }.toList - - /** - * Function add new project - * @param prj project - * @return created project - */ - def addProject(prj:Project, user:PrjUser):OrientGraph => Project = { - tx => { - val map:JavaMap[String, Object] = Map("name" -> prj.name, "startDate" -> prj.startDate.toDate) - val vtx = tx.addVertex("class:Project", map) - findVertexByAttrs(tx, "PhUser", Array("login"), Array(user.login)) match { - case Some(uVtx) => uVtx.addEdge (EdgeNames.Manager, vtx) - case _ => throw new IllegalArgumentException("owner not found: " + user.login) - } - - val Project(p) = vtx - p - } - } - - /** - * Fucntion add new cycle to project. Also with required visits - * @param cycle project cycle - * @return created project cycle - */ - def addPrjCycle (cycle: PrjCycle, prjId:String): OrientGraph => PrjCycle = { - tx => { - val prjVtx = tx.getVertex ("#" + prjId) - if (prjVtx == null) - throw new IllegalArgumentException (s"project {prjId} not found") - - val map:JavaMap[String, Object] = Map("name" -> cycle.name,"startDate" -> cycle.startDate.toDate,"endDate" -> cycle.endDate.toDate) - val vtx = tx.addVertex("class:PrjCycle", map) - val doc = vtx.asDoc - - val visitList:JavaList[ODocument] = {for {visit <- cycle.visits} yield {new ODocument("VisitReq").field("num", visit.num).field("type", visit.typeName)}}.toList - doc.field ("visits", visitList) - - // Add edge - prjVtx.addEdge ("E", vtx) - val PrjCycle(p) = vtx - p - } - } - - def addDrugsToReport (vtx:Vertex, drugs:Iterable[Drug]):Unit = { - val drugList:JavaList[ODocument] = drugs.map( - drug => new ODocument("Drug").field("name", drug.name).field("existence", drug.existence).field("price", drug.price) - ).toList - vtx.asDoc.field ("drugs", drugList) - } - - protected def findOrAddPharmacy (pharm:Pharmacy)(implicit tx:OrientGraph):Vertex = { - findVertexByAttrs(tx, "Pharmacy", Array("cityName", "streetName", "buildingName"), Array(pharm.cityName, pharm.streetName, pharm.buildingName)) match { - case Some(vtx: OrientVertex) => vtx - case _ => addPharmacy(pharm) - } - } - - def findPharmacyByAddress (cityName:String, streetName:String, buildingName:String): OrientGraph => Either[String, Pharmacy] = { - tx => findVertexByAttrs(tx, "Pharmacy", Array("cityName", "streetName", "buildingName"), Array(cityName, streetName, buildingName)) match { - case Some(vtx: OrientVertex) => val Pharmacy (pharm) = vtx; Right(pharm) - case _ => Left("Pharmacy not found") - } - } - - protected def findOrAddPharmNet (pharmNet:PharmNet)(implicit tx:OrientGraph):Vertex = { - findVertexByAttrs(tx, "PharmNet", Array("name"), Array(pharmNet.name)) match { - case Some(vtx: OrientVertex) => vtx - case _ => addPharmNet(pharmNet) - } - } - - protected def addPharmNet (pharmNet:PharmNet)(implicit tx:OrientGraph):Vertex = { - val map:JavaMap[String, Object] = Map("name" -> pharmNet.name, "contract" -> pharmNet.contract) - tx.addVertex ("class:PharmNet", map) - } - - def addPharmacy (pharm:Pharmacy)(implicit tx:OrientGraph):Vertex = { - val map:JavaMap[String, Object] = Map( - "name" -> pharm.name, - "chiefPhone" -> pharm.chiefPhone, - "chiefName" -> pharm.chiefName, - "tradeRoomPhone" -> pharm.tradeRoomPhone, - "cityCode" -> pharm.cityCode, - "cityName" -> pharm.cityName, - "streetCode" -> pharm.streetCode, - "streetName" -> pharm.streetName, - "buildingCode" -> pharm.buildingCode, - "buildingName" -> pharm.buildingName, - "contractExt" -> pharm.contractExt - ) - - val phVtx = tx.addVertex ("class:Pharmacy", map) - // find or add PharmNet - if (pharm.pharmNet != null) { - val phNetVtx = findOrAddPharmNet(pharm.pharmNet) - phVtx.addEdge("PhNet", phNetVtx) - } - - phVtx - } - - def changeReport (rep:Report):OrientGraph => Report = { - tx => { - implicit val gtx = tx - // Get report by id - val repVtx = tx.getVertex ("#" + rep.id) - // Adding drugs to report, if exists - addDrugsToReport (repVtx, rep.drugs) - - // Find or add pharmacy and pharmnet - val phVtx = findOrAddPharmacy (rep.pharmacy) - val phOldVtx = repVtx.out(EdgeNames.ReportPharm) - if (phVtx.id != phOldVtx.id) { - repVtx.getEdges(OUT, EdgeNames.ReportPharm).headOption match { - case Some(edge) => tx.removeEdge(edge) - case None => - } - repVtx.addEdge (EdgeNames.ReportPharm, phVtx) - } - - // Add relationship between pharmacy and project cycle. This relation should be unique - // Only one pharmacy should be used in one project cycle - val cycleVtx = tx.getVertex("#" + rep.cycle.id) - val cycleOldVtx = repVtx.out(EdgeNames.PharmCycle) - if (cycleVtx.id != cycleOldVtx.id) { - cycleVtx.getEdges(OUT, EdgeNames.PharmCycle).headOption match { - case Some(edge) => tx.removeEdge(edge) - case None => - } - cycleVtx.addEdge (EdgeNames.PharmCycle, phVtx) - } - - // Add link to PrjCycle - val cycleOldVtx1 = repVtx.out(EdgeNames.ReportCycle) - if (cycleVtx.id != cycleOldVtx1.id) { - repVtx.getEdges(OUT, EdgeNames.ReportCycle).headOption match { - case Some(edge) => tx.removeEdge(edge) - case None => - } - repVtx.addEdge(EdgeNames.ReportCycle, cycleVtx) - } - - // Add check flag and checker if exists - repVtx.setProperty("checked", rep.checked) - if (rep.checker != null) { - findVertexByAttrs(tx, "PhUser", Array("login"), Array(rep.owner.login)) match { - case Some(vtx) => repVtx.addEdge (EdgeNames.ReportChecker, vtx) - case _ => throw new IllegalArgumentException("checker not found: " + rep.owner.login) - } - } - - val Report(repRet) = repVtx - repRet - } - } - - def addReport (rep:Report):OrientGraph => Report = { - tx => { - implicit val gtx = tx - val map:JavaMap[String, Object] = Map("createDate" -> rep.createDate.toDate) - - // There are several document should be create inside the transaction - // First of all create report vertex - val repVtx = tx.addVertex ("class:Report", map) - // Adding drugs to report, if exists - addDrugsToReport (repVtx, rep.drugs) - - // Find or add pharmacy and pharmnet - val phVtx = findOrAddPharmacy (rep.pharmacy) - repVtx.addEdge (EdgeNames.ReportPharm, phVtx) - - // Add relationship between pharmacy and project cycle. This relation should be unique - // Only one pharmacy should be used in one project cycle - val cycleVtx = tx.getVertex("#" + rep.cycle.id) - cycleVtx.addEdge (EdgeNames.PharmCycle, phVtx) - - // Add link to owner - findVertexByAttrs(tx, "PhUser", Array("login"), Array(rep.owner.login)) match { - case Some(vtx) => repVtx.addEdge (EdgeNames.ReportWorker, vtx) - case _ => throw new IllegalArgumentException("owner not found: " + rep.owner.login) - } - - // Add link to PrjCycle - repVtx.addEdge(EdgeNames.ReportCycle, cycleVtx) - val Report(repRet) = repVtx - repRet - } - } - - def addUserToProject (prjId:String, userId:String, isManager:Boolean):OrientGraph => Boolean = { - tx => { - checkId(prjId) - checkId(userId) - val par:JavaMap[String, Object] = Map("fromV" -> ("#" + prjId), "toV" -> ("#" +userId)) - tx.command(new OCommandSQL(s"CREATE EDGE ${if (isManager) EdgeNames.Manager else EdgeNames.Employee} from :fromV to :toV")).execute(par); - true - } - } - - def removeUserFromProject (prjId:String, userId:String):OrientGraph => Boolean = { - tx => { - checkId(prjId) - checkId(userId) - val par:JavaMap[String, Object] = Map("fromV" -> ("#" + prjId), "toV" -> ("#" + userId)) - tx.command(new OCommandSQL("DELETE EDGE from :fromV to :toV")).execute(par); - true - } - } - - def addUserToPrjCycle (cycleId:String, userId:String):OrientGraph => Boolean = { - tx => { - checkId(cycleId) - checkId(userId) - val par:JavaMap[String, Object] = Map("fromV" -> ("#" + cycleId), "toV" -> ("#" + userId)) - tx.command(new OCommandSQL(s"CREATE EDGE ${EdgeNames.PrjCycleWorker} from :fromV to :toV")).execute(par); - true - } - } - - def removeUserFromPrjCycle (cycleId:String, userId:String):OrientGraph => Boolean = { - tx => { - checkId(cycleId) - checkId(userId) - val par:JavaMap[String, Object] = Map("fromV" -> ("#" + cycleId), "toV" -> ("#" + userId)) - tx.command(new OCommandSQL("DELETE EDGE from :fromV to :toV")).execute(par); - true - } - } - - def findAllPrjUsers(offset:Integer):OrientGraph => List[PrjUser] = { - val sqlQuery = "select * from PhUser limit 200 offset :offset" - tx => { - val par:JavaMap[String, Object] = Map("offset" -> offset) - val vtxs = Query.executeQuery(tx, sqlQuery, par) - for (vtxPrj <- vtxs) yield { val PrjUser (prj) = vtxPrj; prj} - }.toList - } - - def findPrjUserByLogin(login:String):OrientGraph => Either[String, PrjUser] = { - tx => findVertexByAttrs(tx, "PhUser", Array("login"), Array(login)) match { - case Some(vtx) => val PrjUser(user) = vtx; Right(user) - case _ => Left(s"User ${login} not found") - } - } - } -} diff --git a/src/main/scala/org/asem/orient/services/BaseHttpService.scala b/src/main/scala/org/asem/orient/services/BaseHttpService.scala deleted file mode 100644 index e0aecef..0000000 --- a/src/main/scala/org/asem/orient/services/BaseHttpService.scala +++ /dev/null @@ -1,21 +0,0 @@ -package org.asem.orient.services - -import org.asem.spray.security.CookieAuthenticator._ -import org.asem.spray.security.UserData -import spray.http._ -import spray.routing._ - -import scala.concurrent.ExecutionContext.Implicits.global - -/** - * Created by gosha-user on 20.08.2016. - */ -trait BaseHttpService extends HttpService { - def auth(f: UserData => Route): Route = { - authenticate(byCookie) { - user => { - (setCookie(HttpCookie(name = "user_token", content = user.token, maxAge = Some(6000))) compose f).apply(user) - } - } - } -} diff --git a/src/main/scala/org/asem/orient/services/LoginService.scala b/src/main/scala/org/asem/orient/services/LoginService.scala deleted file mode 100644 index 62f64a8..0000000 --- a/src/main/scala/org/asem/orient/services/LoginService.scala +++ /dev/null @@ -1,45 +0,0 @@ -package org.asem.orient.services - -import com.typesafe.config.ConfigFactory -import org.asem.spray.security.CookieAuthenticator._ -import spray.http._ -import MediaTypes._ - -import scala.concurrent.ExecutionContext.Implicits.global - -trait LoginService extends BaseHttpService { - val loginRoute = - path("login") { - post { - auth { - user => respondWithMediaType(`text/html`) { - complete { - -

User named "{user.login}" successfully logged in. Redirecting to main page...

- - - } - } - } - } - } ~ path("logout") { - (get | post) { - authenticate(byCookie) { - user => - deleteCookie("user_token") { - respondWithMediaType(`text/html`) { complete ( - -

User named "{user.login}" successfully logged out. Redirecting to login page...

- - - ) - } - } - } - } - } -} diff --git a/src/main/scala/org/asem/orient/services/MainServiceActor.scala b/src/main/scala/org/asem/orient/services/MainServiceActor.scala deleted file mode 100644 index 8cbccb5..0000000 --- a/src/main/scala/org/asem/orient/services/MainServiceActor.scala +++ /dev/null @@ -1,89 +0,0 @@ -package org.asem.orient.services - -import akka.actor.ActorRef -import com.typesafe.config.ConfigFactory -import spray.can.websocket._ -import spray.can.websocket.frame._ -import spray.http._ -import spray.routing._ - -/** - * Created by gosha-user on 17.07.2016. - * @see http://spray.io/documentation/1.2.2/spray-routing/predefined-directives-by-trait/ - * @see http://spray.io/documentation/1.2.2/spray-routing/scheme-directives/scheme/ - * @see http://spray.io/msug - * @see https://github.com/dcaoyuan/spray-websocket - */ -final case class Push(msg: String) - -class MainServiceActor(val serverConnection: ActorRef) extends HttpServiceActor - with WebSocketServerWorker - with PhUserService - with LoginService - with ReportService - with TaskService - with ProjectService - with StaticResourceService { - // the HttpService trait defines only one abstract member, which - // connects the services environment to the enclosing actor or test - override def actorRefFactory = context - - /** - * login page URL, load from configuration (application.conf) - */ - lazy val loginPage = ConfigFactory.load().getString("loginPage") - - /** - * Override default rejection handler for aithentication rejections. - * Used to automatically redirect to login page. Redirection performs only for get requests - * for other methods respond with Unauthorized exeption - */ - implicit val rh = RejectionHandler { - case AuthenticationFailedRejection(cause, challengeHeaders) :: _ => - get { - redirect(loginPage, StatusCodes.TemporaryRedirect) - } ~ (post | put | delete) { - respondWithStatus(StatusCodes.Unauthorized) { - complete("The resource requires authentication, which was not supplied with the request") - } - } - } - - override def receive = handshaking orElse businessLogicNoUpgrade orElse closeLogic - - def businessLogic: Receive = { - // just bounce frames back for Autobahn testsuite - case x @ (_: BinaryFrame | _: TextFrame) => { - sender() ! x - } - - case Push(msg) => { - println (msg) - send(TextFrame(msg)) - } - - case x: FrameCommandFailed => - log.error("frame command failed", x) - - case x: HttpRequest => // do something - } - - // this actor only runs our route, but you could add - // other things here, like request stream processing - // or timeout handling - def businessLogicNoUpgrade = runRoute( - loginRoute - ~ resourceRoute - ~ userManagementRoute -// ~ reportRoute - ~ taskRoute - ~ projectRouter - ) -} - -trait StaticResourceService extends HttpService { - val resourceRoute = - pathPrefix("pub") { // public directory - getFromDirectory("./src/webapp") - } -} diff --git a/src/main/scala/org/asem/orient/services/PhUserService.scala b/src/main/scala/org/asem/orient/services/PhUserService.scala deleted file mode 100644 index c11cce1..0000000 --- a/src/main/scala/org/asem/orient/services/PhUserService.scala +++ /dev/null @@ -1,272 +0,0 @@ -package org.asem.orient.services - -import com.orientechnologies.orient.core.storage.ORecordDuplicatedException -import com.tinkerpop.blueprints.impls.orient.{OrientGraph, OrientVertex} -import org.asem.orient.model.PhUser -import org.asem.orient.{Database, Query} -import org.asem.spray.security.UserData -import spray.http.MediaTypes._ -import spray.http._ -import spray.httpx.SprayJsonSupport._ -import spray.routing._ - -/** - * Created by gosha-user on 30.07.2016. - */ -object PhUserService extends BaseDB { - /** - * Function create new user and activated it - */ - def createUser(login: String, pasword: String, email: String, firstName: String, secondName: String): Unit = { - val user = PhUser(login, pasword, email, firstName, secondName, Some(true)) - createUser(user) - } - - /** - * Function create user record in database - */ - def createUser(user: PhUser): Either[String, PhUser] = { - try { - Database.getTx( - tx => { - val vtx = addVertex("PhUser", - Map("login" -> user.login, - "password" -> user.pwdHash, - "email" -> user.email, - "firstName" -> user.firstName, - "secondName" -> user.secondName, - "activated" -> user.activated, - "manager" -> user.manager) - )(tx) - Right(user) - } - ) - } - catch { - case ex:ORecordDuplicatedException => Left("duplicate") - } - } - - /** - * Function registedr new user yet no activated - */ - def registerNewUser(login: String, pasword: String, email: String, firstName: String, secondName: String) = { - val user = PhUser(login, pasword, email, firstName, secondName) - createUser(user) - } - - /** - * Fucntion retrieves user vertex by user login - * @param login user login - * @return found user vertex - */ - def findUserByLogin(login: String): Option[OrientVertex] = { - Database.getTx(tx => { - findVertexByAttr(tx, "PhUser", "login", login) - }) - } - - /** - * Function activated inactive user - */ - def activateUser(login: String): Boolean = { - Database.getTx( - graph => { - findVertexByAttr(graph, "PhUser", "login", login) match { - case Some(vtx: OrientVertex) => - vtx.setProperty("activated", true) - vtx.save - true - case _ => false - } - } - ) - } - - /** - * Function update user vertex in database - * @param user user data - * @return success flag - */ - def changeUser(user: PhUser): Boolean = { - try { - Database.getTx( - graph => { - findVertexByAttr(graph, "PhUser", "login", user.login) match { - case Some(vtx: OrientVertex) => - if (user.pwdHash != null && !user.pwdHash.isEmpty) vtx.setProperty("password", user.pwdHash) - if (user.email != null) vtx.setProperty("email", user.email) - if (user.firstName != null) vtx.setProperty("firstName", user.firstName) - if (user.secondName != null) vtx.setProperty("secondName", user.secondName) - if (user.manager.isDefined) vtx.setProperty("manager", user.manager.get) - if (user.activated.isDefined) { - vtx.setProperty("activated", user.activated.get) - println ("user activated: " + vtx.getProperty[Boolean]("activated")) - } - - vtx.save() - true - case _ => - false - } - } - ) - } - catch { - case e:Exception => false - } - } - - /** - * Sample usage of implicit parameters. Function remove vertex from database - * @param vtx vertex to remove - * @param tx implicit parameter OroentGraph object. If not defined new created transaction will be used otherwize gioven tx object - * @return operation status - */ - def deleteUser(vtx: OrientVertex)(implicit tx: OrientGraph = null): Boolean = { - if (tx != null) { - deleteVertex(tx, vtx) - } - else - Database.getTx( - graph => { - deleteVertex(graph, vtx) - } - ) - } - - /** - * Function removes user by it's login. Used deleteUSer function with defined implicit parameter tx = curernt transaction - * @param login login to be removed - * @return operation status - */ - def deleteUserByLogin(login: String): Boolean = { - Database.getTx( - graph => { - findVertexByAttr(graph, "PhUser", "login", login) match { - case Some(vtx: OrientVertex) => - deleteUser (vtx)(tx = graph) - true - case _ => false - } - } - ) - } - - /** - * Function retrieves list of all users - * @return all users from database - */ - def findAllUsers () = { - import java.util.{Collections => JavaCollections} - - import scala.collection.JavaConversions._ - - val result = Query.executeQuery("select * from PhUser", JavaCollections.EMPTY_MAP) - for { - row <- result - } yield { - val PhUser(user) = row - user - } - } -} - -/** - * The trait defines routes for user management service. For all functions except register authentification is required. - * Implemented fucntions: - *
    - *
  • register - add new user
  • - *
  • REST functions for update delete and retrieve user information
  • - *
- */ -trait PhUserService extends BaseHttpService { - private val registerRoute = post { - path("user" / "register") { - entity(as[PhUser]) { - newUser:PhUser => { - if (newUser.email == null || newUser.email.isEmpty) { - respondWithStatus(StatusCodes.BadRequest) { complete("{sucess: false, message: 'Email should be provided'}") } - } - else { - PhUserService.createUser(newUser) match { - case Right(user) => respondWithStatus(StatusCodes.Created) {complete(s"{success: true, message: 'OK'}")} - case Left(error) => respondWithStatus(StatusCodes.Conflict) {complete(s"{success: false, message: '${error}}'")} - } - } - } - } - } - } - - private val changeUserRoute = put { - auth { - user => path ("user" / Segment) { - login => entity(as[PhUser]) { - changePass => { - if (changePass.login != login) { - respondWithStatus(StatusCodes.BadRequest) { complete("Fail") } - } - else { - PhUserService.changeUser(changePass) - complete("{success: true, message: 'OK'}") - } - } - } - } - } - } - - private def isManager (user:UserData, f: StandardRoute):RequestContext => Unit = { - if (user.manager) f - else respondWithStatus(StatusCodes.Forbidden) { complete { "{success: false, message: 'Forbidden " + user.login + " = " + user.manager + "'}" } } - } - - private val deleteUserRoute = delete { - auth { - user => { - path ("user" / Segment) { - login => isManager (user, - complete { - PhUserService.deleteUserByLogin(login) - "{success: true, message: 'OK'}" - } - ) - } - } - } - } - - private val getUserByLoginRoute = get { - auth { - user => path ("user" / Segment) { - login => respondWithMediaType(`application/json`) { - isManager (user, complete { - val vtx = PhUserService.findUserByLogin(login) - if (vtx.isDefined) { - val PhUser(user) = vtx.get - user - } - else { - "{success: false, message: 'no data'}" - } - } - ) - } - } - } - } - - private val userListRoute = get { - path ("user") { - respondWithMediaType(`application/json`) { - complete { - val users = PhUserService.findAllUsers() - users.toArray[PhUser] - } - } - } - } - - val userManagementRoute = registerRoute ~ changeUserRoute ~ deleteUserRoute ~ getUserByLoginRoute ~ userListRoute -} diff --git a/src/main/scala/org/asem/orient/services/ProjectService.scala b/src/main/scala/org/asem/orient/services/ProjectService.scala deleted file mode 100644 index 12f089b..0000000 --- a/src/main/scala/org/asem/orient/services/ProjectService.scala +++ /dev/null @@ -1,132 +0,0 @@ -package org.asem.orient.services - -import org.asem.orient.Database -import org.asem.orient.model.Operation -import org.asem.orient.model.entities._ -import spray.http.MediaTypes._ -import spray.http.StatusCodes._ - -/** - * Created by gosha-user on 03.09.2016. - */ -object ProjectService { - /** - * Get user object by login - * @param login login - * @return project user object - */ - def getUserByLogin (login: String) = Database.getTx (PrjService.findPrjUserByLogin(login)) - - /** - * retrieves user projects - * @param userId user identifier - * @return projects list - */ - def findAllProjects (userId:String): List[Project] = Database.getTx (PrjService.findAllProjects(userId)) - def findActivePrjCycles (prjId:String, userId:String) = Database.getTx (PrjService.findActivePrjCycles(prjId, userId)) - def findCycleReports (cycleId:String) = if (cycleId.isEmpty) List() else Database.getTx (PrjService.findAllReports(cycleId)) - def findCycleReportsForUser (cycleId:String, userId:String) = if (cycleId.isEmpty) List() else Database.getTx (PrjService.findReportsForUser(cycleId, userId)) - def findPharmacyByAddress (cityName:String, streetName:String, buildingName:String) = Database.getTx (PrjService.findPharmacyByAddress(cityName, streetName, buildingName)) - def findPharmNet (cityName:String, streetName:String, buildingName:String) = Database.getTx (PrjService.findPharmacyByAddress(cityName, streetName, buildingName)) - def addUserToProject (prjId:String, userId:String, isManager:Boolean) = Database.getTx (PrjService.addUserToProject(prjId, userId, isManager)) - def addUserToCycle (cycleId:String, userId:String) = Database.getTx (PrjService.addUserToPrjCycle(cycleId, userId)) -} - -trait ProjectService extends BaseHttpService with JacksonJsonSupport { - private val getCurrentProjectUser = get { - auth { - user => path("project" / "curuser") { - respondWithMediaType(`application/json`) ( - ProjectService.getUserByLogin(user.login) match { - case Right(user) => complete (user) - case Left(error) => complete (Operation(error)) - } - ) - } - } - } - - private val getUserProjects = get { - auth { - user => path("project") { - respondWithMediaType(`application/json`) (complete (ProjectService.findAllProjects(user.id))) - } - } - } - - private val getActivePrjCycles = get { - auth { - user => path("cycle") { - parameters('prjId) { - (prjId) => respondWithMediaType(`application/json`)(complete(ProjectService.findActivePrjCycles(prjId, user.id))) - } - } - } - } - - private val getCycleReports = get { - auth { - user => path("report") { - parameters('cycleId) { - (cycleId) => respondWithMediaType(`application/json`)( - user.manager match { - case true => complete(ProjectService.findCycleReports(cycleId)) - case false => complete(ProjectService.findCycleReportsForUser(cycleId, user.id)) - } - ) - } - } - } - } - - private val getPharmacies = get { - auth { - user => path("pharmacy") { - parameters('city, 'street, 'building) { - (city, street, building) => ProjectService.findPharmacyByAddress (city, street, building) match { - case Right(ph) => respondWithMediaType(`application/json`) (complete (ph)) - case Left(error) => respondWithStatus(BadRequest) (complete (Operation(error))) - } - } - } - } - } - - private val addUserToProject = put { - auth { - user => path("project" / Segment / "user") { - prjId => parameters ('userId, 'isManager.as[Boolean]) { - (userId, isManager) => respondWithMediaType(`application/json`)( - ProjectService.addUserToProject(prjId, userId, isManager) match { - case true => complete (Operation("OK", true)) - case false => complete (Operation("Cannot perform operation")) - } - ) - } - } - } - } - - private val addUserToCycle = put { - auth { - user => path("cycle" / Segment / "user") { - cycleId => parameters ('userId) { - (userId) => respondWithMediaType(`application/json`)( - ProjectService.addUserToCycle(cycleId, userId) match { - case true => complete (Operation("OK", true)) - case false => complete (Operation("Cannot perform operation")) - } - ) - } - } - } - } - - val projectRouter = getCurrentProjectUser ~ - getUserProjects ~ - getActivePrjCycles ~ - getCycleReports ~ - getPharmacies ~ - addUserToProject ~ - addUserToCycle -} diff --git a/src/main/scala/org/asem/orient/services/ReportService.scala b/src/main/scala/org/asem/orient/services/ReportService.scala deleted file mode 100644 index 11b8ca0..0000000 --- a/src/main/scala/org/asem/orient/services/ReportService.scala +++ /dev/null @@ -1,94 +0,0 @@ -package org.asem.orient.services - -import org.asem.orient.Database -import org.asem.orient.model.entities.{JacksonJsonSupport, _} -import spray.http._ -import MediaTypes._ - -object ReportService { - def findReportsForUser (cycleId:String, login:String):List[Report] = { - if (cycleId.isEmpty) { - List() - } - else - Database.getTx { - tx => val user = PhUserService.findUserByLogin (login) match { - case Some(vtx) => val PrjUser(usr) = vtx; usr - case _ => throw new IllegalArgumentException(s"User {login} not found") - } - - val cycle = PrjService.findPrjCycleById (cycleId)(tx) - PrjService. findReportsForUser(cycleId, login)(tx) - } - } - - def addReport (login:String, rep:Report):Report = { - Database.getTx { - tx => val user = PhUserService.findUserByLogin (login) match { - case Some(vtx) => val PrjUser(usr) = vtx; usr - case _ => throw new IllegalArgumentException(s"User {login} not found") - } - - PrjService.addReport (rep.copy(owner = user))(tx) - } - } -} - -/** - * Trait represents REST functions to work with reports - */ -trait ReportService extends BaseHttpService with JacksonJsonSupport { - private val listReportsRouter = get { - auth { - user => path("report") { - parameters('cycleId) { - cycleId => respondWithMediaType(`application/json`) (complete (ReportService.findReportsForUser(cycleId, user.login))) - } - } - } - } - - private val addReportsRouter = post { - auth { - user => path("report") { - entity(as[Report]) { - report => respondWithMediaType(`application/json`) (complete (ReportService.addReport(user.login, report))) - } - } - } - } -// -// private val deleteReportRouter = delete { -// auth { -// user => path("report" / Segment) { -// reportId => complete { -// ReportService.deleteById(reportId) -// "OK" -// } -// } -// } -// } -// -// private val getReportRouter = get { -// auth { -// user => path("report" / Segment) { -// reportId => respondWithMediaType(`application/json`) (complete ("{success: true, reportId: " + reportId + "}")) -// } -// } -// } -// -// private val updateReportRouter = put { -// auth { -// user => path("report" / Segment) { -// reportId => entity(as[Report]) { -// report => ReportService.changeReport(reportId, report) match { -// case Left(rep) => respondWithStatus(StatusCodes.OK) { complete(rep) } -// case Right(s) => respondWithStatus(StatusCodes.Conflict) { complete(s) } -// } -// } -// } -// } -// } - - lazy val reportRoute = {}//listReportsRouter ~ addReportsRouter //~ deleteReportRouter ~ getReportRouter ~ updateReportRouter -} diff --git a/src/main/scala/org/asem/spray/security/CookieAuthenticator.scala b/src/main/scala/org/asem/spray/security/CookieAuthenticator.scala deleted file mode 100644 index 0683009..0000000 --- a/src/main/scala/org/asem/spray/security/CookieAuthenticator.scala +++ /dev/null @@ -1,95 +0,0 @@ -package org.asem.spray.security - -import com.typesafe.config.ConfigFactory -import org.asem.orient.Query -import org.asem.orient.model.PhUser -import spray.http.HttpForm -import spray.httpx.unmarshalling._ -import spray.routing.AuthenticationFailedRejection.CredentialsMissing -import spray.routing._ -import spray.routing.authentication._ - -import scala.collection.JavaConversions._ -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.duration._ -import scala.concurrent.{Await, Future} - -case class UserData (login:String, token:String, manager:Boolean, id:String) - -/** - * Created by gosha-user on 30.07.2016. - */ -class CookieAuthenticator { - val config = ConfigFactory.load() - - def loadUser(login: String, pwdHash: String): Option[PhUser] = { - val params = Map( - "login" -> login, - "password" -> pwdHash - ) - - // .toLowerCase() - val result = Query.executeQuery("SELECT * FROM PhUser WHERE login = :login and password = :password", params) - if (result.size() == 1) { - val PhUser(user) = result.get(0) - Some(user) - } - else - None - } - - /** - * Function get information about current user from special cookie (user_token) - * If that cookie not found then get information from query parameters (uname, upass) - * - * @param ctx RequestContext object - * @return extracted credentials if possible - */ - private def extractCredentials(ctx: RequestContext): Option[UserData] = { - val userToken = ctx.request.cookies.find(_.name == "user_token") - var ret: Option[UserData] = None - - if (userToken.isEmpty) { - val formd = ctx.request.as[HttpForm] - if (formd.isRight) { - val user:PhUser = formd.right.get.fields match { - case Seq((name, nameval:String), (pass, passval:String)) => PhUser (nameval, passval) - case _ => null - } - - if (user != null) { - val pu = loadUser(user.login, user.pwdHash) - if (pu.isDefined) { - val user = pu.get - ret = Some(UserData(user.login, RSA.encrypt(user.toString), user.manager.getOrElse(false), user.id)) - } - } - } - } - else { - val userData = Await.result(RSA.decrypt(userToken.get.content), 10.minutes) - val pu = PhUser.fromString (userData) - - if (pu.isDefined) { - val user = pu.get - ret = Some(UserData(user.login, RSA.encrypt(user.toString), user.manager.getOrElse(false), user.id)) - } - } - - ret - } - - /** - * Value (function) used to check & retrieve current credentials - */ - val byCookie: ContextAuthenticator[UserData] = { - ctx => Future { - extractCredentials(ctx) match { - case Some(user) => Right(user) - case _ => Left(AuthenticationFailedRejection(CredentialsMissing, List())) - } - } - } -} - -object CookieAuthenticator extends CookieAuthenticator diff --git a/src/main/scala/ru/sbt/Main.scala b/src/main/scala/ru/sbt/Main.scala new file mode 100644 index 0000000..ea3674f --- /dev/null +++ b/src/main/scala/ru/sbt/Main.scala @@ -0,0 +1,66 @@ +package ru.sbt + +import akka.actor.ActorSystem +import akka.event.Logging +import akka.event.LoggingAdapter +import akka.http.scaladsl.ConnectionContext +import akka.http.scaladsl.Http +import akka.http.scaladsl.server.ExceptionHandler +import akka.stream.ActorMaterializer +import akka.http.scaladsl.server.Directives._ +import akka.http.scaladsl.HttpsConnectionContext +import akka.http.scaladsl.model.HttpResponse +import akka.http.scaladsl.model.StatusCodes._ +import com.softwaremill.session._ +import com.softwaremill.session.CsrfDirectives._ +import com.softwaremill.session.CsrfOptions._ +import com.softwaremill.session.SessionDirectives._ +import com.softwaremill.session.SessionOptions._ + +import java.security.KeyStore +import java.security.SecureRandom +import javax.net.ssl.KeyManagerFactory +import javax.net.ssl.SSLContext +import javax.net.ssl.TrustManagerFactory +import ru.sbt.security.RSA +import ru.sbt.service.MainHttpService +import ru.sbt.utils.Config +import scala.concurrent.ExecutionContext + +object Main extends App with Config { + implicit val actorSystem = ActorSystem() + implicit val executor: ExecutionContext = actorSystem.dispatcher + implicit val logger: LoggingAdapter = Logging(actorSystem, getClass) + implicit val materializer: ActorMaterializer = ActorMaterializer() + + implicit def myExceptionHandler: ExceptionHandler = + ExceptionHandler { + case th: Throwable => + th.printStackTrace + extractUri { uri => + complete(HttpResponse(InternalServerError, entity = th.getMessage)) + } + } + + val mySSLContext: SSLContext = { + val context = SSLContext.getInstance("TLS") + + val keyStore: KeyStore = RSA.keyStore + val keyManagerFactory: KeyManagerFactory = KeyManagerFactory.getInstance("SunX509"); + keyManagerFactory.init(keyStore, RSA.getPassword.getPassword); + + val trustManagerFactory: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509"); + trustManagerFactory.init(keyStore); + + context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom()); + context + } + + val https: HttpsConnectionContext = ConnectionContext.https(mySSLContext) + val httpService = new MainHttpService + val commonRoutes = httpService.router + + Http().bindAndHandle(commonRoutes, httpHost, httpsPort, connectionContext = https) + Http().bindAndHandle(commonRoutes, httpHost, httpPort) +} + diff --git a/src/main/scala/org/asem/orient/Database.scala b/src/main/scala/ru/sbt/orient/Database.scala similarity index 93% rename from src/main/scala/org/asem/orient/Database.scala rename to src/main/scala/ru/sbt/orient/Database.scala index 11c7221..b514c46 100644 --- a/src/main/scala/org/asem/orient/Database.scala +++ b/src/main/scala/ru/sbt/orient/Database.scala @@ -1,4 +1,4 @@ -package org.asem.orient +package ru.sbt.orient import com.tinkerpop.blueprints.impls.orient.{OrientGraph, OrientGraphFactory} import com.typesafe.config.ConfigFactory @@ -15,10 +15,10 @@ object Database { new OrientGraphFactory (config.getString("database.url"), config.getString("database.user"), config.getString("database.password")).setupPool(1, 10) } - def pushMessageToClients (msg:Any) = { - import org.asem.Boot._ - service ! msg - } +// def pushMessageToClients (msg:Any) = { +// import org.asem.Boot._ +// service ! msg +// } def queryToXml (query: String, params:Map[String, Any]) = { getTx ( diff --git a/src/main/scala/ru/sbt/orient/model/JacksonJsonSupport.scala b/src/main/scala/ru/sbt/orient/model/JacksonJsonSupport.scala new file mode 100644 index 0000000..73ad8ff --- /dev/null +++ b/src/main/scala/ru/sbt/orient/model/JacksonJsonSupport.scala @@ -0,0 +1,49 @@ +package ru.sbt.orient.model + +import akka.http.scaladsl.marshalling.Marshaller +import akka.http.scaladsl.model.ContentTypes +import akka.http.scaladsl.model.HttpEntity +import akka.http.scaladsl.model.HttpRequest +import akka.http.scaladsl.model.MediaTypes +import akka.http.scaladsl.unmarshalling.Unmarshaller +import akka.parboiled2.ParserInput +import akka.util.ByteString +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.datatype.joda._ +import com.fasterxml.jackson.module.scala._ +import com.fasterxml.jackson.module.scala.experimental._ +import akka.http.scaladsl.unmarshalling.{ FromByteStringUnmarshaller, FromEntityUnmarshaller, Unmarshaller, FromRequestUnmarshaller , PredefinedFromEntityUnmarshallers} +import akka.http.scaladsl.util.FastFuture +import akka.http.scaladsl.model.MediaTypes._ +import akka.http.scaladsl.model.HttpCharsets._ +import akka.http.scaladsl.marshalling._ +import ru.sbt.orient.model.entities.BaseEntity + +/** + * Created by gosha-user on 27.08.2016. + */ +trait JacksonJsonSupport extends PredefinedFromEntityUnmarshallers { + import com.fasterxml.jackson.annotation.JsonInclude._ + private val jacksonModules = Seq(DefaultScalaModule) + protected val mapper = new ObjectMapper() with ScalaObjectMapper + + // Configure object mapper + mapper.setSerializationInclusion(Include.NON_EMPTY) + mapper.registerModules(jacksonModules:_*) + mapper.registerModule(new JodaModule()) + + implicit def jacksonJsonUnmarshaller[T : Manifest]: FromEntityUnmarshaller[T] = + Unmarshaller.byteStringUnmarshaller.forContentTypes(`application/json`).mapWithCharset { (data, charset) ⇒ + val input = if (charset == `UTF-8`) ParserInput(data.toArray) + else ParserInput(data.decodeString(charset.nioCharset)) + mapper.readValue[T](input.sliceString(0, input.length)) + } + + implicit def jacksonJsonValueMarshaller[T <: BaseEntity]: ToEntityMarshaller[T] = { + Marshaller.StringMarshaller.wrap(`application/json`)(mapper.writeValueAsString) + } + + implicit def jacksonJsonListMarshaller[T <: List[BaseEntity]]: ToEntityMarshaller[T] = { + Marshaller.StringMarshaller.wrap(`application/json`)(mapper.writeValueAsString) + } +} diff --git a/src/main/scala/ru/sbt/orient/model/SbtUser.scala b/src/main/scala/ru/sbt/orient/model/SbtUser.scala new file mode 100644 index 0000000..a9c817b --- /dev/null +++ b/src/main/scala/ru/sbt/orient/model/SbtUser.scala @@ -0,0 +1,74 @@ +package ru.sbt.orient.model + +import java.security._ +import java.util._ + +import com.tinkerpop.blueprints.Vertex +import com.tinkerpop.blueprints.impls.orient.OrientVertex +import ru.sbt.orient.model.entities.BaseEntity +import scala.language.implicitConversions + +/** + * Class represents user for pharmacy input system + * Created by gosha-user on 30.07.2016. + */ +case class SbtUser(login: String, password: String, email: String = "", firstName: String = "", secondName: String = "", activated: Option[Boolean] = None, manager: Option[Boolean] = None, id:String = "") extends BaseEntity { + require(login != null, "login should be provided") + lazy val pwdHash = computeHash(password) + + override def toString = login + "," + email + "," + firstName + "," + secondName + "," + manager.getOrElse(false) + "," + id + + def computeHash(pwd: String): String = { + if (pwd == null || pwd.isEmpty) { + pwd + } + else { + val dig = MessageDigest.getInstance("SHA1").digest(pwd.getBytes) + Base64.getEncoder.encodeToString(dig) + } + } + + def unapply(user:SbtUser) = Some (user.login, user.pwdHash, user.email, user.firstName, user.secondName, user.activated, user.manager, user.id) +} + +/** + * Object used to deserialize PhUser from string, using scala regexp unapply function + */ +object SbtUser { + private val SbtUserRegex = "(.*),(.*),(.*),(.*),(.*),(.*)".r + + def getId[B <: Vertex] (vtx:B):String = { + val id = vtx.getId.toString + id.substring(1, id.length-1) + } + + def fromString(str: String): Option[SbtUser] = str match { + case SbtUserRegex(login, email, firstName, secondName, manager, id) => Some(SbtUser( + login = login, + password = "", + email = email, + firstName = firstName, + secondName = secondName, + activated = Some(true), + id = id, + manager = Some(manager match { + case "null" => false + case s:String => s.toBoolean + case _ => false + }))) + case _ => None + } + + def unapply(vtx: OrientVertex): Option[SbtUser] = Some ( + SbtUser ( + id = getId (vtx), + login = vtx.getProperty[String]("login"), + password = "", + email = vtx.getProperty[String]("email"), + firstName = vtx.getProperty[String]("firstName"), + secondName = vtx.getProperty[String]("secondName"), + activated = Option(vtx.getProperty[Boolean]("activated")), + manager = Option(vtx.getProperty[Boolean]("manager")) + ) + ) +} diff --git a/src/main/scala/ru/sbt/orient/model/entities/package.scala b/src/main/scala/ru/sbt/orient/model/entities/package.scala new file mode 100644 index 0000000..bf47509 --- /dev/null +++ b/src/main/scala/ru/sbt/orient/model/entities/package.scala @@ -0,0 +1,83 @@ +package ru.sbt.orient.model + +/** + * Created by gosha-user on 28.08.2016. + */ +package object entities { + import java.util.Date + + import com.tinkerpop.blueprints.Direction._ + import com.tinkerpop.blueprints.Vertex + import org.joda.time.DateTime + + import scala.collection.JavaConversions._ + import scala.language.implicitConversions + + object DateTimeHelpers { + implicit def date2datetime(date: Date): DateTime = new DateTime(date) + implicit def datetime2date(datetime: DateTime): Date = datetime.toDate + } + + import DateTimeHelpers._ + + object VertexHelpers { + implicit class VertexHelper[B <: Vertex](val vtx:B) { + val id:String = vtx.getId.toString.replace("#", "") + def prop[B](name: String) = vtx.getProperty[B](name) + def out(label: String):Vertex = { + val vtxs = vtx.getVertices(OUT, label) + if (vtxs.iterator().hasNext) + vtxs.iterator().next() + else + null + } + + def outF(label: String, field:String) = { + val vtxs = vtx.getVertices(OUT, label) + if (vtxs.iterator().hasNext) + vtxs.head.prop[String](field) + else + "" + } + + def in(label: String):Vertex = { + val vtxs = vtx.getVertices(IN, label) + if (vtxs.iterator().hasNext) + vtxs.iterator().next() + else + null + } + + def out(label: String*):Iterable[Vertex] = vtx.getVertices(OUT, label:_*) + def in(label: String*):Iterable[Vertex] = vtx.getVertices(IN, label:_*) + } + } + + import VertexHelpers._ + + trait BaseEntity + + case class Comment(id:String = null, owner: String, comment: String, createDate: DateTime) extends BaseEntity + object Comment { + def unapply[B <: Vertex](vtx: B): Option[Comment] = if (vtx != null) Some(Comment(id = vtx.id, owner = vtx.prop("owner"), comment = vtx.prop("comment"), createDate = vtx.prop[Date]("createDate"))) else None + } + + case class Task(id: String, name: String, content: String, comments: Seq[Comment], status: String, assignedPerson: String, changeDate: DateTime, deadLine: DateTime, owner: String) extends BaseEntity + object Task { + def unapply[B <: Vertex](vtx: B): Option[Task] = if (vtx != null) Some( + Task( + id = vtx.id, + name = vtx.prop("name"), + content = vtx.prop("content"), + comments = {for {com:Vertex <- vtx.out()} yield {val Comment(c) = com; c}}.toList, + status = vtx.prop("status"), + assignedPerson = vtx.prop("assignedPerson"), + changeDate = vtx.prop[Date]("changeDate"), + deadLine = vtx.prop[Date]("deadLine"), + owner = vtx.prop("owner") + )) + else None + } + + case class RequestStatus (success: Boolean, msg:String = null, entity:Any = null) extends BaseEntity +} diff --git a/src/main/scala/ru/sbt/security/OrientDBAuthenticator.scala b/src/main/scala/ru/sbt/security/OrientDBAuthenticator.scala new file mode 100644 index 0000000..55dff4d --- /dev/null +++ b/src/main/scala/ru/sbt/security/OrientDBAuthenticator.scala @@ -0,0 +1,75 @@ +package ru.sbt.security + +import com.softwaremill.session.SessionSerializer +import com.softwaremill.session.MultiValueSessionSerializer +import ru.sbt.orient.Query +import ru.sbt.orient.model.SbtUser + +import scala.collection.JavaConversions._ +import scala.concurrent.duration._ +import scala.concurrent._ +import akka.event.LoggingAdapter +import akka.http.impl.util._ +import akka.http.scaladsl.server.directives.CookieDirectives._ +import akka.http.scaladsl.common._ +import akka.http.scaladsl.server._ +import akka.http.scaladsl.util.FastFuture._ +import akka.http.scaladsl.unmarshalling._ +import akka.http.scaladsl.server.AuthenticationFailedRejection +import akka.http.scaladsl.server.AuthenticationFailedRejection._ +import scala.util.Try + +case class UserData (login:String, manager:Boolean, id:String) + +object UserData { + implicit def serializer: SessionSerializer[UserData, String] = new MultiValueSessionSerializer( + toMap = {t: UserData => Map( + "userName" -> t.login, + "id" -> t.id, + "manager" -> t.manager.toString + ) + }, + fromMap = {m: Map[String, String] => Try(UserData(m("userName"), m("manager").toBoolean, m("id")))} + ) +} + +/** + * Created by gosha-user on 30.07.2016. + */ +trait OrientDBAuthenticator { + private def loadUser(login: String, pwdHash: String): Option[SbtUser] = { + val params = Map( + "login" -> login, + "password" -> pwdHash + ) + + val result = Query.executeQuery("SELECT * FROM PhUser WHERE login = :login and password = :password", params) + if (result.size() == 1) { + val vtx = result.get(0) + val SbtUser(user) = result.get(0) + Some(user) + } + else + None + } + + /** + * Function get information about current user from special cookie (user_token) + * If that cookie not found then get information from query parameters (uname, upass) + * + * @param ctx RequestContext object + * @return extracted credentials if possible + */ + def loadUserData(userName:String, userPwd:String)(implicit logger: LoggingAdapter): Option[UserData] = { + val user = SbtUser (userName, userPwd) + val pu = loadUser(user.login, user.pwdHash) + pu match { + case Some(usr) => + if (logger.isDebugEnabled) + logger.debug ("user found: " + usr.login + ", " + usr.toString) + Some(UserData(usr.login, usr.manager.getOrElse(false), usr.id)) + + case _ => None + } + } +} diff --git a/src/main/scala/org/asem/spray/security/RSA.scala b/src/main/scala/ru/sbt/security/RSA.scala similarity index 58% rename from src/main/scala/org/asem/spray/security/RSA.scala rename to src/main/scala/ru/sbt/security/RSA.scala index 92deba3..59a3848 100644 --- a/src/main/scala/org/asem/spray/security/RSA.scala +++ b/src/main/scala/ru/sbt/security/RSA.scala @@ -1,4 +1,4 @@ -package org.asem.spray.security +package ru.sbt.security import java.io.FileInputStream import java.security.KeyStore @@ -6,13 +6,14 @@ import java.util.Base64 import javax.crypto.Cipher import com.typesafe.config.ConfigFactory -import spray.caching._ +import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.concurrent.duration._ import scala.language.postfixOps - +import java.util.{Map, LinkedHashMap} +import scala.concurrent._ /** * Created by gosha-user on 30.07.2016. @@ -38,7 +39,26 @@ object RSA { keyStore.getEntry(alias, keyPwd).asInstanceOf[KeyStore.PrivateKeyEntry] } - private val cacheKeys:Cache[String] = new ExpiringLruCache[String](10000, 0, 30 minutes, 10 minutes) + def makeCache[K,V](capacity: Int): Map[K, V] = { + new LinkedHashMap[K, V](capacity, 0.7F, true) { + private val cacheCapacity = capacity + override def removeEldestEntry(entry: Map.Entry[K, V]): Boolean = { + this.size() > this.cacheCapacity + } + } + } + + private lazy val cacheKeys:Map[String, String] = makeCache(10000) + private def cache (key:String)(cachedFunc:String => Future[String]):Future[String] = { + if (cacheKeys.containsKey(key)) { + Future(cacheKeys.get(key)) + } + else { + val value = cachedFunc (key) + cacheKeys.put (key, Await.result (value, 10.seconds)) + value + } + } def isInitialized:Boolean = { pkEntry.getPrivateKey() != null @@ -51,11 +71,13 @@ object RSA { Base64.getEncoder.encodeToString(cipherData) } - def decrypt(data:String): Future[String] = cacheKeys(data) { - val cipher = Cipher.getInstance("RSA") - cipher.init(Cipher.DECRYPT_MODE, pkEntry.getPrivateKey) - val ret = new String(cipher.doFinal(Base64.getDecoder.decode(data))) - ret + def decrypt(data:String): Future[String] = cache(data) { + data => { + val cipher = Cipher.getInstance("RSA") + cipher.init(Cipher.DECRYPT_MODE, pkEntry.getPrivateKey) + val ret = new String(cipher.doFinal(Base64.getDecoder.decode(data))) + Future(ret) + } } def remove (data: String):Unit = { diff --git a/src/main/scala/org/asem/orient/services/BaseDB.scala b/src/main/scala/ru/sbt/service/BaseDB.scala similarity index 97% rename from src/main/scala/org/asem/orient/services/BaseDB.scala rename to src/main/scala/ru/sbt/service/BaseDB.scala index e6f4771..0d14af9 100644 --- a/src/main/scala/org/asem/orient/services/BaseDB.scala +++ b/src/main/scala/ru/sbt/service/BaseDB.scala @@ -1,4 +1,4 @@ -package org.asem.orient.services +package ru.sbt.service import com.tinkerpop.blueprints.impls.orient.{OrientEdge, OrientGraph, OrientVertex} import org.joda.time.DateTime @@ -13,7 +13,6 @@ trait BaseDB { var vtx = tx.addVertex("class:" + clazz, java.util.Collections.EMPTY_MAP) vertexUpdate(vtx, params) vtx.save() - tx.commit() vtx } } diff --git a/src/main/scala/ru/sbt/service/MainHttpService.scala b/src/main/scala/ru/sbt/service/MainHttpService.scala new file mode 100644 index 0000000..45fa891 --- /dev/null +++ b/src/main/scala/ru/sbt/service/MainHttpService.scala @@ -0,0 +1,118 @@ +package ru.sbt.service + +import ru.sbt.orient.model.SbtUser +import ru.sbt.orient.model.entities.RequestStatus +import ru.sbt.security.OrientDBAuthenticator +import ru.sbt.security.UserData +import ru.sbt.service._ +import scala.concurrent.ExecutionContext.Implicits.global +import akka.http.scaladsl.server.Directives._ +import akka.http.scaladsl.server.RejectionHandler +import akka.http.scaladsl.server.directives._ +import akka.actor.ActorSystem +import akka.event.LoggingAdapter +import akka.http.scaladsl.model.StatusCodes._ +import akka.http.scaladsl.model.headers.HttpChallenges +import akka.http.scaladsl.server.AuthenticationFailedRejection +import akka.http.scaladsl.server.AuthenticationFailedRejection._ +import akka.stream.Materializer +import com.typesafe.config.ConfigFactory +import org.apache.commons.lang.RandomStringUtils +import ru.sbt.orient.model.JacksonJsonSupport + +case class LoginData (login:String, pass:String) + +class MainHttpService(implicit val logger: LoggingAdapter, implicit val im: Materializer, implicit val as: ActorSystem) + extends MainHttpServiceTrait + with OrientDBAuthenticator + with WSService { + import com.softwaremill.session._ + import com.softwaremill.session.CsrfDirectives._ + import com.softwaremill.session.CsrfOptions._ + import com.softwaremill.session.SessionDirectives._ + import com.softwaremill.session.SessionOptions._ + + val nextSessionId = RandomStringUtils.randomAscii(64) + val sessionConfig = SessionConfig.default(nextSessionId) + implicit val sessionManager = new SessionManager[UserData](sessionConfig) + implicit val refreshTokenStorage = new InMemoryRefreshTokenStorage[UserData] { + def log(msg: String) = logger.debug(msg) + } + + def mySetSession(v: UserData) = setSession(refreshable, usingCookies, v) + val myRequiredSession = requiredSession(refreshable, usingCookies) + val myInvalidateSession = invalidateSession(refreshable, usingCookies) + + lazy val loginPage = ConfigFactory.load().getString("loginPage") + + val rh = RejectionHandler.newBuilder().handle { + case AuthenticationFailedRejection(cause, challengeHeaders) => redirect(loginPage, TemporaryRedirect) + }.result().withFallback(RejectionHandler.default) + + val loginRoute = pathPrefix("api") { + path("do_login") { + post { + logger.debug(s"Trying to login") + entity(as[LoginData]) {loginData => + logger.debug(s"Logging in $loginData.login") + + val userOpt = loadUserData(loginData.login, loginData.pass) + userOpt match { + case Some(user) => mySetSession(user) { + setNewCsrfToken(checkHeader) { + complete(OK -> RequestStatus (success = true)) + } + } + + case _ => val chal = HttpChallenges.oAuth2("example") + complete (Unauthorized) + } + } + } + } ~ + path("do_logout") { + post { + myRequiredSession { session => + myInvalidateSession { + logger.debug(s"Logging out $session") + complete(OK -> RequestStatus (success = true)) + } + } + } + } ~ + path("current_login") { + get { + myRequiredSession { session => + logger.debug(s"Current session: $session.login") + complete(OK -> RequestStatus (success = true, entity = session)) + } + } + } ~ + path("do_register") { + post { + logger.debug(s"Trying to register user") + entity(as[SbtUser]) { newUser => + if (newUser.email == null || newUser.email.isEmpty) { + complete(BadRequest -> RequestStatus (false, "Email should be provided")) + } + else { + val createdUser = UserService.createUser(newUser) + complete(Created -> RequestStatus (success = true, entity = createdUser)) + } + } + } + } + } + + lazy val router = randomTokenCsrfProtection(checkHeader) { + handleRejections(rh) { + routes ~ loginRoute ~ myRequiredSession {websocketRoute} + } + } +} + +trait MainHttpServiceTrait extends JacksonJsonSupport { + val routes = pathPrefix("pub") { + getFromDirectory ("./src/webapp") + } +} diff --git a/src/main/scala/org/asem/orient/services/TaskService.scala b/src/main/scala/ru/sbt/service/TaskService.scala similarity index 50% rename from src/main/scala/org/asem/orient/services/TaskService.scala rename to src/main/scala/ru/sbt/service/TaskService.scala index a9e3cb1..5025a61 100644 --- a/src/main/scala/org/asem/orient/services/TaskService.scala +++ b/src/main/scala/ru/sbt/service/TaskService.scala @@ -1,28 +1,51 @@ -package org.asem.orient.services - -import java.util.Date +package ru.sbt.service +import scala.concurrent.ExecutionContext.Implicits.global import com.tinkerpop.blueprints.Direction._ -import com.tinkerpop.blueprints.Vertex import com.tinkerpop.blueprints.impls.orient.OrientVertex -import org.asem.orient.Database -import org.asem.orient.model.entities.{Comment, JacksonJsonSupport, Task, EdgeNames} -import spray.http._ -import spray.httpx.SprayJsonSupport._ -import spray.routing.RequestContext +import ru.sbt.orient.Database +import ru.sbt.orient.model.entities._ +import ru.sbt.orient.model._ import scala.collection.JavaConversions._ -import org.asem.Boot.{PushToChildren, service => mainService} + +sealed trait TaskOperations { + def name:String + def func(task:Task, userId:String):Any + def unapply(value: String): Option[TaskOperations] = if (name == value) Some(this) else None +} + +case object AddTask extends TaskOperations { + val name = "ADD" + def func (task:Task, userId:String) = TaskService.addTask(task, userId) +} + +case object DelTask extends TaskOperations { + val name = "DEL" + def func (task:Task, userId:String) = TaskService.deleteTask(task.id, userId) +} + +case object ChangeTask extends TaskOperations { + val name = "CHANGE" + def func (task:Task, userId:String) = TaskService.changeTask(task, userId) +} + +case object AddComment extends TaskOperations { + val name = "COMMENT" + def func (task:Task, userId:String) = for {comment <- task.comments} yield {TaskService.addComment(task.id, comment, comment.owner)} +} + +case class TaskOperation(operation:String, task: Task) object TaskService extends BaseDB { /** + * https://github.com/google/code-prettify + * http://markup.su/highlighter/ * Function ertrieves all task from database * @return list of tasks with comments */ def findAllTasks = Database.getTx {tx => val vtxs = tx.getVerticesOfClass("Task"); for {vtx <- vtxs} yield {val Task (task) = vtx;task}}.toList.sortWith((a:Task,b:Task) => a.changeDate.isAfter(b.changeDate)) - private def notifyByWebSocket (msg:String) = { if (mainService != null) mainService ! PushToChildren(msg) } - private def task2map (task:Task) = Map( "name" -> task.name, "content" -> task.content, "status" -> task.status, "assignedPerson" -> task.assignedPerson, "changeDate" -> task.changeDate, "deadLine" -> task.deadLine, "owner" -> task.owner ) @@ -35,8 +58,8 @@ object TaskService extends BaseDB { Database.getTx( tx => { val vtx = addVertex("Task", task2map(task)).apply(tx) + tx.commit() val Task(ret) = vtx - notifyByWebSocket("TASKS_UPDATED:ADDED") ret } ) @@ -58,7 +81,6 @@ object TaskService extends BaseDB { vtx.save graph.commit() - notifyByWebSocket("TASKS_UPDATED:CHANGED") Left(task) } case _ => Right("Error updating record: " + "#" + task.id) @@ -79,7 +101,6 @@ object TaskService extends BaseDB { case vtx: OrientVertex => { graph.removeVertex(vtx) graph.commit() - notifyByWebSocket("TASKS_UPDATED:DELETED") Left("DELETE SUCCESSFULL") } case _ => Right("Error deleting record: " + "#" + taskId) @@ -98,14 +119,16 @@ object TaskService extends BaseDB { graph.getVertex("#" + taskId) match { case vtx: OrientVertex => { val comVtx = addVertex("Comment", Map( - "owner" -> graph.getVertex("#" + userId).getProperty[String]("login"), + "owner" -> (graph.getVertex("#" + userId) match { + case userVtx:OrientVertex => userVtx.getProperty[String]("login") + case _ => "" + }), "comment" -> comment.comment, "createDate" -> comment.createDate )).apply(graph) vtx.addEdge("com", comVtx) graph.commit() - notifyByWebSocket("TASKS_UPDATED:COMMENT_ADDED") Left(comment) } case _ => Right("Error add comment to task: " + "#" + taskId) @@ -113,78 +136,4 @@ object TaskService extends BaseDB { } ) } -} - -trait TaskService extends BaseHttpService with JacksonJsonSupport { - private val listTasksRouter = get { - auth { - user => path("task") { - complete { - val tasks = TaskService.findAllTasks - tasks - } - } - } - } - - private val addTaskRouter = post { - auth { - user => path("task") { - entity(as[Task]) { - task => { - val t = TaskService.addTask(task, user.id) - println (t) - complete {t} - } - } - } - } - } - - private val deleteTaskRouter = delete { - auth { - user => path("task" / Segment) { - reportId => { - TaskService.deleteTask(reportId, user.id) match { - case Left(rep) => respondWithStatus(StatusCodes.OK ) { complete (rep)} - case Right(s) => respondWithStatus(StatusCodes.InternalServerError) { complete (s) } - } - } - } - } - } - - private val updateTaskRouter = put { - auth { - user => path("task" / Segment) { - reportId => { - entity(as[Task]) { - task => TaskService.changeTask(task, user.id) match { - case Left(rep) => respondWithStatus(StatusCodes.OK ) { complete (rep)} - case Right(s) => respondWithStatus(StatusCodes.Conflict) { complete (s) } - } - } - } - } - } - } - - private val addTaskCommentRouter = post { - auth { - user => path("task" / Segment / "comment") { - taskId => { - entity(as[Comment]) { - comment => { - TaskService.addComment(taskId, comment, user.id) match { - case Left(com) => respondWithStatus(StatusCodes.OK ) { complete (com)} - case Right(s) => respondWithStatus(StatusCodes.Conflict) { complete (s) } - } - } - } - } - } - } - } - - lazy val taskRoute = listTasksRouter ~ addTaskRouter ~ deleteTaskRouter ~ updateTaskRouter ~ addTaskCommentRouter -} +} \ No newline at end of file diff --git a/src/main/scala/ru/sbt/service/UserService.scala b/src/main/scala/ru/sbt/service/UserService.scala new file mode 100644 index 0000000..ecf8529 --- /dev/null +++ b/src/main/scala/ru/sbt/service/UserService.scala @@ -0,0 +1,159 @@ +package ru.sbt.service + +import com.tinkerpop.blueprints.impls.orient.OrientGraph +import com.tinkerpop.blueprints.impls.orient.OrientVertex +import ru.sbt.orient.Database +import ru.sbt.orient.Query +import ru.sbt.orient.model.SbtUser + +object UserService extends BaseDB { + /** + * Function create new user and activated it + */ + def createUser(login: String, pasword: String, email: String, firstName: String, secondName: String): Unit = { + val user = SbtUser(login, pasword, email, firstName, secondName, Some(true)) + createUser(user) + } + + /** + * Function create user record in database + */ + def createUser(user: SbtUser): SbtUser = { + val SbtUser(createdUser) = Database.getTx( + addVertex("PhUser", + Map("login" -> user.login, + "password" -> user.pwdHash, + "email" -> user.email, + "firstName" -> user.firstName, + "secondName" -> user.secondName, + "activated" -> user.activated, + "manager" -> user.manager) + ) + ) + + createdUser + } + + /** + * Function registedr new user yet no activated + */ + def registerNewUser(login: String, pasword: String, email: String, firstName: String, secondName: String) = { + val user = SbtUser(login, pasword, email, firstName, secondName) + createUser(user) + } + + /** + * Fucntion retrieves user vertex by user login + * @param login user login + * @return found user vertex + */ + def findUserByLogin(login: String): Option[OrientVertex] = { + Database.getTx(tx => { + findVertexByAttr(tx, "SbtUser", "login", login) + }) + } + + /** + * Function activated inactive user + */ + def activateUser(login: String): Boolean = { + Database.getTx( + graph => { + findVertexByAttr(graph, "PhUser", "login", login) match { + case Some(vtx: OrientVertex) => + vtx.setProperty("activated", true) + vtx.save + true + case _ => false + } + } + ) + } + + /** + * Function update user vertex in database + * @param user user data + * @return success flag + */ + def changeUser(user: SbtUser): Boolean = { + try { + Database.getTx( + graph => { + findVertexByAttr(graph, "PhUser", "login", user.login) match { + case Some(vtx: OrientVertex) => + if (user.pwdHash != null && !user.pwdHash.isEmpty) vtx.setProperty("password", user.pwdHash) + if (user.email != null) vtx.setProperty("email", user.email) + if (user.firstName != null) vtx.setProperty("firstName", user.firstName) + if (user.secondName != null) vtx.setProperty("secondName", user.secondName) + if (user.manager.isDefined) vtx.setProperty("manager", user.manager.get) + if (user.activated.isDefined) { + vtx.setProperty("activated", user.activated.get) + println ("user activated: " + vtx.getProperty[Boolean]("activated")) + } + + vtx.save() + true + case _ => + false + } + } + ) + } + catch { + case e:Exception => false + } + } + + /** + * Sample usage of implicit parameters. Function remove vertex from database + * @param vtx vertex to remove + * @param tx implicit parameter OroentGraph object. If not defined new created transaction will be used otherwize gioven tx object + * @return operation status + */ + def deleteUser(vtx: OrientVertex)(implicit tx: OrientGraph = null): Boolean = { + if (tx != null) { + deleteVertex(tx, vtx) + } + else + Database.getTx( + graph => { + deleteVertex(graph, vtx) + } + ) + } + + /** + * Function removes user by it's login. Used deleteUSer function with defined implicit parameter tx = curernt transaction + * @param login login to be removed + * @return operation status + */ + def deleteUserByLogin(login: String): Boolean = { + Database.getTx( + graph => { + findVertexByAttr(graph, "PhUser", "login", login) match { + case Some(vtx: OrientVertex) => + deleteUser (vtx)(tx = graph) + true + case _ => false + } + } + ) + } + + /** + * Function retrieves list of all users + * @return all users from database + */ + def findAllUsers () = { + import java.util.{Collections => JavaCollections} + import scala.collection.JavaConversions._ + + val result = Query.executeQuery("select * from PhUser", JavaCollections.EMPTY_MAP) + for { + row <- result + } yield { + val SbtUser(user) = row + user + } + } +} diff --git a/src/main/scala/ru/sbt/service/WSService.scala b/src/main/scala/ru/sbt/service/WSService.scala new file mode 100644 index 0000000..a49edfd --- /dev/null +++ b/src/main/scala/ru/sbt/service/WSService.scala @@ -0,0 +1,20 @@ +package ru.sbt.service + +import akka.actor.ActorSystem +import akka.stream.Materializer +import akka.http.scaladsl.server.Directives +import akka.http.scaladsl.server.directives.WebSocketDirectives +import ru.sbt.service.ws.ProjectRooms +import ru.sbt.security.UserData + +trait WSService extends Directives with WebSocketDirectives { + implicit val im: Materializer + implicit val as: ActorSystem + + def websocketRoute = {user:UserData => path("ws-project" / Segment) { + projectId => handleWebSocketMessages( + ProjectRooms.findOrCreate(projectId.toInt).websocketFlow(user.login) + ) + } + } +} diff --git a/src/main/scala/ru/sbt/service/ws/ChatEvents.scala b/src/main/scala/ru/sbt/service/ws/ChatEvents.scala new file mode 100644 index 0000000..63fd146 --- /dev/null +++ b/src/main/scala/ru/sbt/service/ws/ChatEvents.scala @@ -0,0 +1,17 @@ +package ru.sbt.service.ws + +import akka.actor.ActorRef + +case class ChatMessage(sender: String, text: String) + +object SystemMessage { + def apply(text: String) = ChatMessage("System", text) +} + +sealed trait ChatEvent +case class UserJoined(name: String, userActor: ActorRef) extends ChatEvent +case class UserLeft(name: String) extends ChatEvent +case class IncomingMessage(sender: String, message: String) extends ChatEvent + + + diff --git a/src/main/scala/ru/sbt/service/ws/ProjectRoom.scala b/src/main/scala/ru/sbt/service/ws/ProjectRoom.scala new file mode 100644 index 0000000..edaadcf --- /dev/null +++ b/src/main/scala/ru/sbt/service/ws/ProjectRoom.scala @@ -0,0 +1,67 @@ +package ru.sbt.service.ws + +import akka.actor._ +import akka.http.scaladsl.model.ws.{ Message, TextMessage } +import akka.stream.FlowShape +import akka.stream.OverflowStrategy +import akka.stream.scaladsl._ + +class ProjectRoom(roomId: Int, actorSystem: ActorSystem) { + private[this] val chatRoomActor = actorSystem.actorOf(Props(classOf[ProjectRoomActor], roomId)) + + def websocketFlow(user: String): Flow[Message, Message, Any] = { + val graph = GraphDSL.create(Source.actorRef[ChatMessage](5, OverflowStrategy.fail)) { + implicit builder: GraphDSL.Builder[ActorRef] => + import GraphDSL.Implicits._ + source => + + // flow used as input, it takes Messages + val fromWebsocket = builder.add( + Flow[Message].collect { + case TextMessage.Strict(txt) => IncomingMessage(user, txt) + } + ) + + // flow used as output, it returns Messages + val backToWebsocket = builder.add( + Flow[ChatMessage].map { + case ChatMessage(author, text) => TextMessage(s"[$author]: $text") + } + ) + + // send messages to the actor, if sent also UserLeft(user) before stream completes. + val messengerActorSink = Sink.actorRef[ChatEvent](chatRoomActor, UserLeft("left")) + + // a merger for two pipes + val merge = builder.add(Merge[ChatEvent](2)) + + //Materialized value of Actor who sits in the chatroom + val actorAsSource = builder.materializedValue.map(actor => UserJoined(user, actor)) + + fromWebsocket ~> merge.in(0) + + //If Source actor is just created, it should be sent as UserJoined and registered as particiant in the room + + actorAsSource ~> merge.in(1) + + //Merges both pipes above and forwards messages to chatroom represented by ChatRoomActor + + merge ~> messengerActorSink + + //Actor already sits in chatRoom so each message from room is used as source and pushed back into the websocket + + source ~> backToWebsocket + + FlowShape(fromWebsocket.in, backToWebsocket.out) + } + + //Factory method allows for materialization of this Source + Flow.fromGraph(graph) + } + + def sendMessage(message: ChatMessage): Unit = chatRoomActor ! message +} + +object ProjectRoom { + def apply(roomId: Int)(implicit actorSystem: ActorSystem) = new ProjectRoom(roomId, actorSystem) +} \ No newline at end of file diff --git a/src/main/scala/ru/sbt/service/ws/ProjectRoomActor.scala b/src/main/scala/ru/sbt/service/ws/ProjectRoomActor.scala new file mode 100644 index 0000000..27f2dfc --- /dev/null +++ b/src/main/scala/ru/sbt/service/ws/ProjectRoomActor.scala @@ -0,0 +1,78 @@ +package ru.sbt.service.ws + +import akka.actor.{Actor, ActorRef} +import akka.event.Logging +import akka.event.LoggingAdapter +import ru.sbt.orient.model.JacksonJsonSupport +import ru.sbt.orient.model.entities.Task +import ru.sbt.orient.model.entities.Comment +import ru.sbt.service.AddComment +import ru.sbt.service.AddTask +import ru.sbt.service.ChangeTask +import ru.sbt.service.DelTask +import ru.sbt.service.TaskOperation + +class ProjectRoomActor(roomId: Int) extends Actor with JacksonJsonSupport { + var participants: Map[String, ActorRef] = Map.empty[String, ActorRef] + val logger: LoggingAdapter = Logging.getLogger(context.system, this) + + override def receive: Receive = { + case UserJoined(name, actorRef) => + participants += name -> actorRef + broadcast(SystemMessage(s"User $name joined channel...")) + + case UserLeft(name) => + if (logger.isDebugEnabled) + logger.debug(s"User $name left channel[$roomId]") + + broadcast(SystemMessage(s"User $name left channel[$roomId]")) + participants -= name + + case msg: IncomingMessage => + val TaskDTORegex = "\\{(.*)\\}".r + println (msg.message) + + msg.message match { + // For object messages + case TaskDTORegex(task) => + val taskDTO:TaskOperation = mapper.readValue[TaskOperation] (msg.message) + taskDTO.operation match { + case AddTask(task) => + val t = task.func(taskDTO.task, msg.sender) + broadcast(ChatMessage (msg.sender, "New task added: " + t.asInstanceOf[Task].id)) + + case DelTask(task) => + val t = task.func(taskDTO.task, msg.sender) + t.asInstanceOf[Either[String, String]] match { + case Left(s) => broadcast(ChatMessage (msg.sender, "Task deleted successfull")) + case Right(s)=> broadcast(ChatMessage (msg.sender, "Error deleting task: " + s)) + } + + case ChangeTask(task) => + val t = task.func(taskDTO.task, msg.sender) + t.asInstanceOf[Either[Task, String]] match { + case Left(s) => broadcast(ChatMessage (msg.sender, "Task changed: " + s.name)) + case Right(s) => broadcast(ChatMessage (msg.sender, "Error changing task: " + s)) + } + + case AddComment(task) => + val t = task.func(taskDTO.task, msg.sender) + t match { + case x:List[Either[Comment, String]@unchecked] => x.foreach { + case Left(s) => broadcast(ChatMessage (msg.sender, "Comment added: " + s)) + case Right(s) => broadcast(ChatMessage (msg.sender, "Error adding comment: " + s)) + } + } + + case _ => + if (logger.isDebugEnabled) + logger.debug(s"Unknow task operation: $taskDTO.operation") + } + + // For simple text messages + case _ => broadcast(ChatMessage (msg.sender, msg.message)) + } + } + + def broadcast(message: ChatMessage): Unit = participants.values.foreach(_ ! message) +} diff --git a/src/main/scala/ru/sbt/service/ws/ProjectRooms.scala b/src/main/scala/ru/sbt/service/ws/ProjectRooms.scala new file mode 100644 index 0000000..a89bc5c --- /dev/null +++ b/src/main/scala/ru/sbt/service/ws/ProjectRooms.scala @@ -0,0 +1,15 @@ +package ru.sbt.service.ws + +import akka.actor.ActorSystem + +object ProjectRooms { + var projectRooms: Map[Int, ProjectRoom] = Map.empty[Int, ProjectRoom] + + def findOrCreate(number: Int)(implicit actorSystem: ActorSystem): ProjectRoom = projectRooms.getOrElse(number, createNewChatRoom(number)) + + private def createNewChatRoom(number: Int)(implicit actorSystem: ActorSystem): ProjectRoom = { + val chatroom = ProjectRoom(number) + projectRooms += number -> chatroom + chatroom + } +} diff --git a/src/main/scala/ru/sbt/service/ws/package.scala b/src/main/scala/ru/sbt/service/ws/package.scala new file mode 100644 index 0000000..c312f9a --- /dev/null +++ b/src/main/scala/ru/sbt/service/ws/package.scala @@ -0,0 +1,7 @@ +package ru.sbt.service.ws + +import scala.language.implicitConversions + +package object chat { + implicit def chatEventToChatMessage(event: IncomingMessage): ChatMessage = ChatMessage(event.sender, event.message) +} diff --git a/src/main/scala/ru/sbt/utils/Config.scala b/src/main/scala/ru/sbt/utils/Config.scala new file mode 100644 index 0000000..ac34c67 --- /dev/null +++ b/src/main/scala/ru/sbt/utils/Config.scala @@ -0,0 +1,17 @@ +package ru.sbt.utils + +import com.typesafe.config.ConfigFactory + +trait Config { + private val config = ConfigFactory.load() + private val httpConfig = config.getConfig("http") + private val databaseConfig = config.getConfig("database") + + val httpHost = httpConfig.getString("interface") + val httpPort = httpConfig.getInt("port") + val httpsPort = httpConfig.getInt("sslPort") + + val jdbcUrl = databaseConfig.getString("url") + val dbUser = databaseConfig.getString("user") + val dbPassword = databaseConfig.getString("password") +} diff --git a/src/main/scala/ru/sbt/utils/CorsSupport.scala b/src/main/scala/ru/sbt/utils/CorsSupport.scala new file mode 100644 index 0000000..43fa057 --- /dev/null +++ b/src/main/scala/ru/sbt/utils/CorsSupport.scala @@ -0,0 +1,39 @@ +package ru.sbt + +import akka.http.scaladsl.model.HttpMethods._ +import akka.http.scaladsl.model.HttpResponse +import akka.http.scaladsl.model.headers._ +import akka.http.scaladsl.server.Directives._ +import akka.http.scaladsl.server.{ Directive0, Route } +import com.typesafe.config.ConfigFactory + +trait CorsSupport { + lazy val allowedOriginHeader = { + val config = ConfigFactory.load() + val sAllowedOrigin = config.getString("cors.allowed-origin") + if (sAllowedOrigin == "*") + `Access-Control-Allow-Origin`.* + else + `Access-Control-Allow-Origin`(HttpOrigin(sAllowedOrigin)) + } + + private def addAccessControlHeaders: Directive0 = { + mapResponseHeaders { headers => + allowedOriginHeader +: + `Access-Control-Allow-Credentials`(true) +: + `Access-Control-Allow-Headers`("Token", "Content-Type", "X-Requested-With") +: + headers + } + } + + private def preflightRequestHandler: Route = options { + complete(HttpResponse(200).withHeaders( + `Access-Control-Allow-Methods`(OPTIONS, POST, PUT, GET, DELETE) + ) + ) + } + + def corsHandler(r: Route) = addAccessControlHeaders { + preflightRequestHandler ~ r + } +} \ No newline at end of file diff --git a/src/test/scala/ru/sbt/orient/services/LoginServiceTest.scala b/src/test/scala/ru/sbt/orient/services/LoginServiceTest.scala deleted file mode 100644 index 8296482..0000000 --- a/src/test/scala/ru/sbt/orient/services/LoginServiceTest.scala +++ /dev/null @@ -1,69 +0,0 @@ -package org.asem.orient.services - -import org.scalatest._ -import spray.http.StatusCodes._ -import spray.http._ -import spray.routing._ -import spray.testkit._ - -import scala.concurrent.duration._ -import scala.language.postfixOps - -class LoginServiceTest extends FlatSpec - with Matchers - with Directives - with ScalatestRouteTest - with LoginService { - - def actorRefFactory = system - implicit val defaultTimeout = RouteTestTimeout(10 seconds) - - // Prepare data - PhUserService.findUserByLogin("user") match { - case Some(user) => None - case _ => PhUserService.createUser( - login = "user", - pasword = "user", - email = "user@demo.com", - firstName = "Demo", - secondName = "Demos" - ) - } - - "Login REST service" should "return unauthorized (401) if no credentials provided" in { - Post("/login", FormData(Seq("uname" -> "", "upass" -> ""))) ~> sealRoute(loginRoute) ~> check { - status should equal(Unauthorized) - } - } - - it should "permanent redirect for success logged user" in { - Post("/login", FormData(Seq("uname" -> "user", "upass" -> "user"))) ~> sealRoute(loginRoute) ~> check { - status should equal(OK) - } - } - - it should "fail logging user with wrong credentials" in { - Post("/login", FormData(Seq("uname" -> "user", "upass" -> "xxxx"))) ~> sealRoute(loginRoute) ~> check { - status should equal(Unauthorized) - } - } - - it should "success logging user with name in wrong case" in { - Post("/login", FormData(Seq("uname" -> "User", "upass" -> "user"))) ~> sealRoute(loginRoute) ~> check { - status should equal(OK) - } - } - - it should "fail if using get instead of put http method" in { - Get("/login?uname=User&upass=user") ~> sealRoute(loginRoute) ~> check { - status should equal(MethodNotAllowed) - } - } - -// it should "success logout logged user" in { -// Get("/logout") ~> loginRoute ~> check { -// responseAs[String] should equal("The user was logged out") -// header[`Set-Cookie`] should equal(Some(`Set-Cookie`(HttpCookie("userName", content = "deleted", expires = Some(DateTime.MinValue))))) -// } -// } -} diff --git a/src/test/scala/ru/sbt/orient/services/PhUserServiceTest.scala b/src/test/scala/ru/sbt/orient/services/PhUserServiceTest.scala deleted file mode 100644 index f0a2116..0000000 --- a/src/test/scala/ru/sbt/orient/services/PhUserServiceTest.scala +++ /dev/null @@ -1,110 +0,0 @@ -package org.asem.orient.services - -import org.asem.orient.model._ -import org.scalatest._ -import spray.http.HttpHeaders._ -import spray.http.StatusCodes._ -import spray.http._ -import spray.httpx.SprayJsonSupport._ -import spray.json.DefaultJsonProtocol._ -import spray.routing._ -import spray.testkit._ - -class PhUserServiceTest extends FlatSpec - with Matchers - with Directives - with ScalatestRouteTest - with LoginService - with PhUserService { - def actorRefFactory = system - - def setTestCookie(name:String, value:String):HttpRequest ⇒ HttpRequest = { - req => { - req.withHeaders(`Cookie`(HttpCookie(name, value))) - } - } - - "User service rest" should "register and return new created user" in { - val user = Map("login" -> "demo", "password" -> "demo", "email" -> "demo@demo.com") - Post("/user/register", user) ~> sealRoute(userManagementRoute) ~> check { - status should equal(Created) - responseAs[String] should equal("{success: true, message: 'OK'}") - } - } - - it should "generate Bad Request rejection if email is not provided" in { - val user = Map("login" -> "demo", "password" -> "demo") - Post("/user/register", user) ~> sealRoute(userManagementRoute) ~> check { - status should equal(BadRequest) - val resp = responseAs[String] - } - } - - var cookie = "" - it should "login with new created user" in { - Post("/login", FormData(Seq("uname" -> "demo", "upass" -> "demo"))) ~> sealRoute(loginRoute) ~> check { - status should equal(OK) - cookie = header[`Set-Cookie`] match { - case Some(`Set-Cookie`(HttpCookie("user_token", content, _, _, _, _, _, _, _))) => content - case _ => "" - } - - cookie shouldNot be ("") - } - } - - it should "throw an exception when trying to change password for another user" in { - val user = Map("login" -> "demo1", "password" -> "demo", "email" -> "demo@demo.com") - Put("/user/demo", user) ~> setTestCookie ("user_token", cookie) ~> sealRoute(userManagementRoute) ~> check { - status should equal(BadRequest) - } - } - - it should "successfully change manager flag for user demo" in { - val user = PhUser( - login = "demo", - password = "demo", - email = "demo@demo.com", - manager = Some(true), - firstName = "user", - secondName = "demonstration", - activated = Some(true) - ) - Put("/user/demo", user) ~> setTestCookie ("user_token", cookie) ~> sealRoute(userManagementRoute) ~> check { - status should equal(OK) - } - } - - it should "relogin with changed user as manager" in { - Post("/login", FormData(Seq("uname" -> "demo", "upass" -> "demo"))) ~> sealRoute(loginRoute) ~> check { - status should equal(OK) - cookie = header[`Set-Cookie`] match { - case Some(`Set-Cookie`(HttpCookie("user_token", content, _, _, _, _, _, _, _))) => content - case _ => "" - } - - cookie shouldNot be ("") - } - } - - it should "return list fo users for authorized user" in { - Get("/user") ~> setTestCookie ("user_token", cookie) ~> sealRoute(userManagementRoute) ~> check { - status should equal(OK) -// println (responseAs[String]) - } - } - - it should "return user information by login" in { - Get("/user/demo") ~> setTestCookie ("user_token", cookie) ~> sealRoute(userManagementRoute) ~> check { - status should equal(OK) -// println (responseAs[String]) - } - } - - it should "remove user by login" in { - Delete("/user/demo") ~> setTestCookie ("user_token", cookie) ~> sealRoute(userManagementRoute) ~> check { - status should equal(OK) -// println (responseAs[String]) - } - } -} diff --git a/src/test/scala/ru/sbt/orient/services/ReportServiceTest.scala b/src/test/scala/ru/sbt/orient/services/ReportServiceTest.scala deleted file mode 100644 index 568fd48..0000000 --- a/src/test/scala/ru/sbt/orient/services/ReportServiceTest.scala +++ /dev/null @@ -1,233 +0,0 @@ -package org.asem.orient.services - -import org.asem.orient.Database -import org.asem.orient.model.entities._ -import org.joda.time.DateTime -import org.scalatest._ -import spray.routing._ -import spray.testkit._ - -/** - * Created by gosha-user on 13.08.2016. - */ -class ReportServiceTest extends FlatSpec - with Matchers - with Directives - with ScalatestRouteTest - with LoginService - with ReportService { - def actorRefFactory = system - - "Report" should "create initial data" in { - Database.getTx { - tx => { - val user = PrjUser (login = "user") - - val prj = PrjService.addProject(Project(name = "test project", startDate = new DateTime()), user)(tx) - val pc = PrjService.addPrjCycle ( - PrjCycle ( - name = "Цикл-1", - startDate = new DateTime(), - endDate = new DateTime().plusDays(10), - visits = List[VisitReq]( - VisitReq ( - typeName = "P", - num = 21 - ) - ) - ), prj.id)(tx) - - val rep = Report( - createDate = DateTime.now(), - cycle = pc, - pharmacy = Pharmacy ( - name = "36.6", - chiefPhone = "100-1001-0", - chiefName = "Chief", - tradeRoomPhone = "111-322-322", - pharmNet = PharmNet(name = "36.6", contract = "No contract"), - cityCode = "1001", - cityName = "New-York", - streetCode = "10020", - streetName = "Vishington st", - buildingCode = "101", - buildingName = "234" - ), - owner = user, - drugs = List(Drug(name = "Analgin", existence = true, price = 99.99)), - checker = null - ) - - PrjService.addReport (rep)(tx) - - tx.rollback - } - } - } - } -// -// val prjs = PrjService.findAllProjects(PrjUser("#33:0", "user")).apply(tx) -//// println (prjs) -// -// val prjc = PrjService.findActivePrjCycles(prjs.head, PrjUser("#33:0", "user")).apply(tx).head -// println (prjc) -// -// var reps = PrjService.findReportsForUser (prjc, PrjUser("#33:0", "user")).apply(tx) -// println ("Reps1: " + Report.write(reps.head)) -// -// reps = PrjService.findAllReports (prjc).apply(tx) -// println ("reps2: " + Report.write(reps.head)) -// } -// } -// -// val pc = PrjCycle ( -// id = "101", -// name = "name", -// startDate = new DateTime(), -// endDate = new DateTime(), -// visits = List[VisitReq]() -// ) -// -// val pu = PrjUser (login = "user", -// email = "user@demo.com", -// firstName = "Demo", -// secondName = "Demos", -// id = "#33:2" -// ) -// -// val ph = Pharmacy ( -// id = "String", -// name = "String", -// chiefPhone = "String", -// chiefName = "String", -// tradeRoomPhone = "String", -// pharmNet = null, -// cityCode = "String", -// cityName = "String", -// streetCode = "String", -// streetName = "String", -// buildingCode = "String", -// buildingName = "String" -// ) -// -// val rep = Report( -// "100", -// pc, -// pu, -// ph, -// List[Drug](), -// true, -// pu -// ) -// -// val json = Report.write (rep) -//// println (json) -// -// val rep1:Report = Report.read (json) -//// println (rep1) -// } - -// def setTestCookie(name:String, value:String):HttpRequest ⇒ HttpRequest = { -// req => { -// req.withHeaders(`Cookie`(HttpCookie(name, value))) -// } -// } -// - -// -// "Report service" should "convert all fields of report object to map in runtime" in { -// val map = Report.entity2Map(rep) -// map should not be empty -// } -// -// var repSaved:Report = _ -// it should "create new report row" in { -// ReportService.createReport(rep.login, rep) match { -// case Left(v) => -// v should not be null -// repSaved = v match { -// case x:Report => x -// } -// case Right(s) => fail("error creating report " + s) -// } -// } -// -// it should "thrown an error when trying to create new report with the same address" in { -// ReportService.createReport(rep.login, rep) match { -// case Left(v) => fail("test not passed new record created") -// case Right(err) => err should be("duplicate_index") -// } -// } -// -// it should "delete report by id" in { -// val err = ReportService.deleteById (repSaved) -// err should be (true) -// } -// -// "Report http service" should "reject request without user credentials" in { -// Get("/report") ~> sealRoute(reportRoute) ~> check { -// status should equal(Unauthorized) -// } -// } -// -// var cookie = "" -// it should "login successfully with demo user" in { -// Post("/login", FormData(Seq("uname" -> "user", "upass" -> "user"))) ~> sealRoute(loginRoute) ~> check { -// status should equal(OK) -// cookie = header[`Set-Cookie`] match { -// case Some(`Set-Cookie`(HttpCookie("user_token", content, _, _, _, _, _, _, _))) => content -// case _ => "" -// } -// -// cookie shouldNot be ("") -// } -// } -// -// var resp:JsValue = _ -// it should "create new report fro currently logged user" in { -// Post("/report", rep) ~> setTestCookie ("user_token", cookie) ~> sealRoute(reportRoute) ~> check { -// status should equal(Created) -// val Report(repo) = JsonParser(responseAs[String]) match { -// case o:JsObject => o -// } -// -// repo.login should be("user") -// } -// } -// -// it should "retrieve all reports for currently logged user" in { -// Get("/report") ~> setTestCookie ("user_token", cookie) ~> sealRoute(reportRoute) ~> check { -// status should equal(OK) -// resp = JsonParser(responseAs[String]) -// val Report(repo) = resp match { -// case o:JsObject => o -// case JsArray(a) => a.head.asJsObject -// } -// -// repSaved = repo -// } -// } -// -// it should "update fields in report object" in { -// val user = Map("street" -> "lenina") -// Put("/report/" + repSaved.id, user) ~> setTestCookie ("user_token", cookie) ~> sealRoute(reportRoute) ~> check { -// println (responseAs[String]) -// -// status should equal(OK) -// resp = JsonParser(responseAs[String]) -// val Report(repo) = resp match { -// case o:JsObject => o -// case JsArray(a) => a.head.asJsObject -// } -// -// repSaved = repo -// repSaved.street should be ("lenina") -// } -// } -// -// it should "delete report by report id" in { -// Delete("/report/" + repSaved.id) ~> setTestCookie ("user_token", cookie) ~> sealRoute(reportRoute) ~> check { -// status should equal(OK) -// } -// } - diff --git a/src/test/scala/ru/sbt/orient/services/TaskServiceTest.scala b/src/test/scala/ru/sbt/orient/services/TaskServiceTest.scala deleted file mode 100644 index 184024b..0000000 --- a/src/test/scala/ru/sbt/orient/services/TaskServiceTest.scala +++ /dev/null @@ -1,91 +0,0 @@ -package org.asem.orient.services - -import org.asem.orient.model.entities.{Comment, JsonMapper, Task} -import org.joda.time.DateTime -import org.scalatest._ -import spray.http.HttpHeaders._ -import spray.http.StatusCodes._ -import spray.http._ -import spray.httpx.SprayJsonSupport._ -import spray.json._ -import spray.routing._ -import spray.testkit._ - -import scala.concurrent.duration._ -import scala.language.postfixOps - -class TaskServiceTest extends FlatSpec - with Matchers - with Directives - with ScalatestRouteTest - with LoginService - with TaskService -{ - def actorRefFactory = system - implicit val defaultTimeout = RouteTestTimeout(10 seconds) - - def setTestCookie(name:String, value:String):HttpRequest ⇒ HttpRequest = { - req => { - req.withHeaders(`Cookie`(HttpCookie(name, value))) - } - } - - "Task service" should "reject unsecuren requests when get task list called" in { - Get("/task") ~> sealRoute(taskRoute) ~> check { - status should equal(Unauthorized) - } - } - - var cookie = "" - it should "login successfully with demo user" in { - Post("/login", FormData(Seq("uname" -> "user", "upass" -> "user"))) ~> sealRoute(loginRoute) ~> check { - status should equal(OK) - cookie = header[`Set-Cookie`] match { - case Some(`Set-Cookie`(HttpCookie("user_token", content, _, _, _, _, _, _, _))) => content - case _ => "" - } - - cookie shouldNot be ("") - } - } - - var savedTask:Task = null - it should "return list of tasks for secured GET HTTP request" in { - Get("/task") ~> setTestCookie ("user_token", cookie) ~> sealRoute(taskRoute) ~> check { - status should equal(OK) - savedTask = responseAs[List[Task]].head - } - } - - it should "create add comemnt to task" in { - val com = Comment( - owner = "vasya", - comment = "Ничего не понял, повторите пож-та", - createDate = new DateTime - ) - - println ("savedTask : " +savedTask) - Post("/task/" + savedTask.id + "/comment", com) ~> setTestCookie ("user_token", cookie) ~> sealRoute(taskRoute) ~> check { - status should equal(OK) - println (responseAs[Comment]) - } - } - - it should "create new Task through the Post HTTP request" in { - val task = Task( - id = null, - name = "Тестовая задача", - content = "Что то пошло не так", - status = "Новый", - assignedPerson = "user", - changeDate = new DateTime(), - deadLine = new DateTime(), - owner = "user", - comments = null) - - Post("/task", task) ~> setTestCookie ("user_token", cookie) ~> sealRoute(taskRoute) ~> check { - status should equal(OK) - println (responseAs[Task]) - } - } -} diff --git a/src/test/scala/ru/sbt/orient/services/UserServiceTest.scala b/src/test/scala/ru/sbt/orient/services/UserServiceTest.scala deleted file mode 100644 index a5a4573..0000000 --- a/src/test/scala/ru/sbt/orient/services/UserServiceTest.scala +++ /dev/null @@ -1,37 +0,0 @@ -package org.asem.orient.services - -import com.tinkerpop.blueprints.impls.orient.OrientVertex -import org.scalatest._ - -class UserServiceTest extends FlatSpec with Matchers { - - "User" should "be stored in database" in { - PhUserService.createUser( - login = "demo", - pasword = "demo", - email = "demo@demo.com", - firstName = "Demo", - secondName = "Demos" - ) - } - - var user:OrientVertex = _ - it should "be found in database " in { - user = PhUserService.findUserByLogin("demo") match { - case Some(user) => user - case _ => null - } - - user shouldNot be(null) - } - - "Found user" should "has login equals to demo" in { - val login:String = user.getProperty("login") - login should be("demo") - } - - it should "be deleted from database" in { - val flag = PhUserService.deleteUser (user) - flag should be(true) - } -} diff --git a/src/test/scala/ru/sbt/orient/services/XMLTest.scala b/src/test/scala/ru/sbt/orient/services/XMLTest.scala deleted file mode 100644 index 5eb9d6a..0000000 --- a/src/test/scala/ru/sbt/orient/services/XMLTest.scala +++ /dev/null @@ -1,75 +0,0 @@ -package org.asem.orient.services - -import org.asem.orient._ -import org.asem.orient.model.PhUser -import org.asem.spray.security.RSA -import org.scalatest._ - -import scala.concurrent.Await -import scala.concurrent.duration._ -import scala.language.postfixOps - -class XMLTest extends FlatSpec with Matchers { - - "XML" should "be properly generated from array of map" in { - val data = Array( - Map("name" -> "test1", "value" -> "test_value1"), - Map("name" -> "test1", "value" -> "test_value1") - ) - - val xml = - {for { - row <- data - } yield { - - {for {prop <- row.keys} yield { - - }} - - }} - - -// val printer = new PrettyPrinter(80, 2) -// println(printer.format(xml)) - } - - it should "read from database and store to XML" in { - val xml = Database.queryToXml("SELECT * FROM PhUser", Map()) - val printer = new scala.xml.PrettyPrinter(80, 2) -// println(printer.format(xml)) - } - - it should "read from database and store to XML with different root element" in { - val xml = Database.queryToXml("child", "SELECT * FROM PhUser", Map()) - val printer = new scala.xml.PrettyPrinter(80, 2) -// println(printer.format(xml)) - } - - "RSA" should "load keystore from file" in { - RSA.isInitialized should be (true) - } - - var encryptedData = "" - it should "encrypt data using public key" in { - encryptedData = RSA.encrypt("test data") - encryptedData shouldNot be ("") -// println (encryptedData) - } - - it should "decrypt data using private key" in { - val decryptedData = Await.result (RSA.decrypt(encryptedData), 1 minutes) - decryptedData should be ("test data") -// println (decryptedData) - } - - val user = PhUser(login = "demo", password = "demo", email = "demo@demo.org", firstName = "firstname", secondName = "secondName") - "PhUser" should "be printed to string" in { - user.toString should be ("demo,demo@demo.org,firstname,secondName,false,") - } - - it should "be unapplied from string" in { - val restoredUser = PhUser.fromString(user.toString).orNull - restoredUser.toString should be (user.toString) - restoredUser.pwdHash should be ("") - } -} \ No newline at end of file diff --git a/src/test/scala/ru/sbt/services/LoginServiceTest.scala b/src/test/scala/ru/sbt/services/LoginServiceTest.scala new file mode 100644 index 0000000..cbcc3d0 --- /dev/null +++ b/src/test/scala/ru/sbt/services/LoginServiceTest.scala @@ -0,0 +1,50 @@ +package org.asem.orient.services + +import org.scalatest._ +import akka.http.scaladsl.model.FormData +import akka.http.scaladsl.model.HttpRequest +import akka.http.scaladsl.model.StatusCodes +import akka.http.scaladsl.testkit.ScalatestRouteTest +import akka.http.scaladsl.model.headers.HttpCookie +import akka.http.scaladsl.server._ +import Directives._ +import ru.sbt.service.MainHttpServiceTrait +import akka.http.scaladsl.model.headers._ + +class LoginServiceTest extends FlatSpec + with Matchers + with Directives + with ScalatestRouteTest + with MainHttpServiceTrait { + + +// def setTestCookie(name:String, value:String):HttpRequest ⇒ HttpRequest = { +// req => { +// req.withHeaders(`Cookie`(name, value)) +// } +// } + +// var cookie = "" +// "Login REST service" should "authorize with credentials provided" in { +// Post("/", FormData(Map("uname" -> "user", "upass" -> "user"))) ~> routes ~> check { +// println (status) +// cookie = header[`Set-Cookie`] match { +// case Some(`Set-Cookie`(HttpCookie("user_token", content, _, _, _, _, _, _, _))) => content +// case _ => "" +// } +// println (cookie) +// } +// } +// +// it should "decrypt token" in { +// Get("/") ~> setTestCookie ("user_token", cookie) ~> routes ~> check { +// println (status) +// } +// } +// +// it should "get tocken from cache" in { +// Get("/") ~> setTestCookie ("user_token", cookie) ~> routes ~> check { +// println (status) +// } +// } +} diff --git a/src/webapp/app/app.js b/src/webapp/app/app.js deleted file mode 100644 index 9c7f0f4..0000000 --- a/src/webapp/app/app.js +++ /dev/null @@ -1,14 +0,0 @@ -Ext.application({ - name: 'PH', - // automatically create an instance of PH.view.Viewport - autoCreateViewport: true, - controllers: [ - 'App' - ], - models:[ - 'Menu' - ], - stores:[ - 'Menu' - ] -}); diff --git a/src/webapp/app/app/controller/App.js b/src/webapp/app/app/controller/App.js deleted file mode 100644 index 6e8fddb..0000000 --- a/src/webapp/app/app/controller/App.js +++ /dev/null @@ -1,66 +0,0 @@ - -Ext.define("PH.controller.App", { - extend: 'Ext.app.Controller', - - refs: [{ - ref: 'contentPanel', - selector: 'contentPanel' - }], - - init: function() { - this.control ({ - 'navigation': { - selectionchange: this.onNavSelectionChange - } - }); - }, - onNavSelectionChange: function(selModel, records) { - var record = records[0], - xtype = record.get('module'), - alias = 'widget.' + xtype; - - if (!this.getContentPanel) - return; - - var contentPanel = this.getContentPanel(); - contentPanel.removeAll(true); - contentPanel.setTitle (null); - - if (xtype) { - var className = Ext.ClassManager.getNameByAlias(alias); - if (!className) { - if (console) - console.error ('Not found module: ' + xtype); - - return; - } - - // load controller class - var viewClass = Ext.ClassManager.get(className); - var cmp = new viewClass(); - - contentPanel.add(cmp); - cmp.setTitle (PH.utils.CommonUtils.getLocaleString('titles', xtype)); - if (cmp.isXType('grid')) { - var columns = PH.utils.CommonUtils.createColumnFromModel(GLocale, cmp.getStore()) - cmp.reconfigure(cmp.store, columns); - PH.utils.CommonUtils.refreshAnyGrid(cmp) - } - else { - var grid = cmp.down('grid') - var columns = PH.utils.CommonUtils.createColumnFromModel(GLocale, grid.getStore()) - grid.reconfigure(grid.store, columns); - PH.utils.CommonUtils.refreshAnyGrid(grid) - } - - if (cmp.floating) { - cmp.show(); - } - else { - if (this.centerContent) - this.centerContent(); - } - } - } -}); - diff --git a/src/webapp/app/app/locale/en.js b/src/webapp/app/app/locale/en.js deleted file mode 100644 index 097109c..0000000 --- a/src/webapp/app/app/locale/en.js +++ /dev/null @@ -1,56 +0,0 @@ -Ext.define('PH.locale.en', { - singleton: true, - menu: { - security: 'Security', - users: 'Users', - groups: 'Groups', - settings: 'Settings', - menu: 'Menus', - projects: 'Projects', - cycles: 'Cycles', - reports: "Reports" - }, - titles: { - navi: 'Navigation Menu', - users: 'Users', - reports: 'Reports', - projects: 'Projects' - }, - select: { - project: 'Choose project', - cycle: 'Choose cycle' - }, - fields: { - name: 'Name', - startDate: 'Start date', - login: 'login', - firstName: 'firsName', - secondName: 'secondName', - manager: 'manager', - activated: 'activated', - email: 'email', - city: 'City', - sreet: 'Street', - building: 'Building', - pharmNet: 'PharmNet', - pharmacy: 'Pharmacy', - agreements: 'Agreements', - managerName: 'Manager name', - managerPhone: 'Manager phone', - tradeRoomPhone: 'Traderoom phone' - }, - button: { - save: 'Save', - cancel: 'Cancel', - delete: 'Delete', - close: 'Close', - refresh: 'Refresh', - add: 'Add' - }, - messages: { - wait: 'Please wait...' - } -}); - - - diff --git a/src/webapp/app/app/locale/ru.js b/src/webapp/app/app/locale/ru.js deleted file mode 100644 index 994d587..0000000 --- a/src/webapp/app/app/locale/ru.js +++ /dev/null @@ -1,54 +0,0 @@ -Ext.define('PH.locale.ru', { - singleton: true, - menu: { - security: 'Безопасность', - users: 'Пользователи', - groups: 'Группы', - settings: 'Настройки', - menu: 'Меню', - projects: 'Проекты', - cycles: 'Циклы' - }, - titles: { - navi: 'Функции системы', - users: 'Пользователи системы', - userEdit: 'Редактирование пользователя', - reports: 'Отчеты', - projects: 'Проекты' - }, - select: { - project: 'Выбор проекта', - cycle: 'Выбор цикла' - }, - fields: { - name: 'Название', - startDate: 'Дата старта', - login: 'Пользователь', - firstName: 'Имя', - secondName: 'Фамилия', - manager: 'Менеджер', - activated: 'Активный', - email: 'Почта', - city: 'Город', - sreet: 'Улица', - building: 'Дом', - pharmNet: 'Сеть', - pharmacy: 'Атека', - agreements: 'Договоренность', - managerName: 'Заведующий', - managerPhone: 'Тел. завед.', - tradeRoomPhone: 'Торг.зал.тел' - }, - button: { - save: 'Сохранить', - cancel: 'Отмена', - delete: 'Удалить', - close: 'Закрыть', - refresh: 'Обновить', - add: 'Добавить' - }, - messages: { - wait: 'Пожалуйста подождите...' - } -}); - diff --git a/src/webapp/app/app/model/Menu.js b/src/webapp/app/app/model/Menu.js deleted file mode 100644 index d4786b8..0000000 --- a/src/webapp/app/app/model/Menu.js +++ /dev/null @@ -1,10 +0,0 @@ -Ext.define('PH.model.Menu', { - extend: 'Ext.data.Model', - fields: [{ - name: 'name', - convert: function (newValue) { - return GLocale.menu[newValue] ? GLocale.menu[newValue] : newValue; - } - }, "leaf"] -}); - diff --git a/src/webapp/app/app/model/PrjCycle.js b/src/webapp/app/app/model/PrjCycle.js deleted file mode 100644 index 0147dfd..0000000 --- a/src/webapp/app/app/model/PrjCycle.js +++ /dev/null @@ -1,37 +0,0 @@ -Ext.define('PH.model.PrjCycle', { - extend: 'Ext.data.Model', - idProperty: 'id', - fields: [{ - name: 'id', - identifier: true - }, - 'name', { - name: 'startDate', - type: 'date', - convert: function (dt) {return new Date(dt);} - }, { - name: 'endDate', - type: 'date', - convert: function (dt) {return new Date(dt);} - }, { - name: 'visitPharm', - convert: function (v, record) { - for (visit in record.visits) { - if (visit.typeName === 'P') { - return visit.num; - } - } - } - }, { - name: 'visitDoctor', - convert: function (v, record) { - for (visit in record.visits) { - if (visit.typeName === 'D') { - return visit.num; - } - } - } - } - ] -}); - diff --git a/src/webapp/app/app/model/Project.js b/src/webapp/app/app/model/Project.js deleted file mode 100644 index 7d4cb76..0000000 --- a/src/webapp/app/app/model/Project.js +++ /dev/null @@ -1,18 +0,0 @@ -Ext.define('PH.model.Project', { - extend: 'Ext.data.Model', - idProperty: 'id', - fields: [{ - name: 'id', - identifier: true - }, - 'name', - { - name: 'startDate', - type: 'date', - convert: function (dt) { - return new Date(dt); - } - } - ] -}); - diff --git a/src/webapp/app/app/model/Report.js b/src/webapp/app/app/model/Report.js deleted file mode 100644 index 28ff35d..0000000 --- a/src/webapp/app/app/model/Report.js +++ /dev/null @@ -1,26 +0,0 @@ -Ext.define('PH.model.Report', { - extend: 'Ext.data.Model', - idProperty: 'id', - fields: [ - 'login', - 'city', - 'street', - 'building', - 'pharmNet', - 'pharmacy', - 'agreements', - 'managerName', - { - name: 'managerPhone' - // validators: { - // type: 'format', - // matcher: /(\d{3})\d{3}\-\d{4}/ - // } - }, - 'tradeRoomPhone', - { - name: 'id', - identifier: true - }] -}); - diff --git a/src/webapp/app/app/model/User.js b/src/webapp/app/app/model/User.js deleted file mode 100644 index 54441dc..0000000 --- a/src/webapp/app/app/model/User.js +++ /dev/null @@ -1,17 +0,0 @@ -Ext.define('PH.model.User', { - extend: 'Ext.data.Model', - - requires: [ - 'Ext.data.reader.Json' - ], - - idProperty: 'login', - fields: ['login', 'firstName', 'secondName', 'email', - { - name: 'activated', - type: 'bool' - }, { - name: 'manager', - type: 'bool' - }] -}); \ No newline at end of file diff --git a/src/webapp/app/app/store/Menu.js b/src/webapp/app/app/store/Menu.js deleted file mode 100644 index 7ba9b8b..0000000 --- a/src/webapp/app/app/store/Menu.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2014 Mes Solutions. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -Ext.define('PH.store.Menu', { - extend: 'Ext.data.TreeStore', - autoLoad: true, - model: 'PH.model.Menu', - root: { - id: -1, - name: 'root' - }, - proxy: { - type: 'ajax', - actionMethods: { - read: 'GET' - }, - api: { - read: '/pub/app/data/menu.json' - }, - reader: { - type: 'json', - rootProperty: 'menuItemList' - } - } -}); - diff --git a/src/webapp/app/app/store/PrjCycle.js b/src/webapp/app/app/store/PrjCycle.js deleted file mode 100644 index e93c443..0000000 --- a/src/webapp/app/app/store/PrjCycle.js +++ /dev/null @@ -1,20 +0,0 @@ -Ext.define('PH.store.PrjCycle', { - extend: 'Ext.data.Store', - model: 'PH.model.PrjCycle', - autoLoad: false, - alias: 'store.prjCycles', - cycleId: null, - - proxy: { - type: 'rest', - api: { - read: '/cycle', - update: '/cycle', - delete: '/cycle', - create: '/cycle' - }, - reader: { - type: 'json' - } - } -}); \ No newline at end of file diff --git a/src/webapp/app/app/store/Project.js b/src/webapp/app/app/store/Project.js deleted file mode 100644 index 6572e78..0000000 --- a/src/webapp/app/app/store/Project.js +++ /dev/null @@ -1,20 +0,0 @@ -Ext.define('PH.store.Project', { - extend: 'Ext.data.Store', - model: 'PH.model.Project', - autoLoad: false, - alias: 'store.projects', - cycleId: null, - - proxy: { - type: 'rest', - api: { - read: '/project', - update: '/project', - delete: '/project', - create: '/project' - }, - reader: { - type: 'json' - } - } -}); \ No newline at end of file diff --git a/src/webapp/app/app/store/Reports.js b/src/webapp/app/app/store/Reports.js deleted file mode 100644 index 4d994a9..0000000 --- a/src/webapp/app/app/store/Reports.js +++ /dev/null @@ -1,23 +0,0 @@ -Ext.define('PH.store.Reports', { - extend: 'Ext.data.Store', - model: 'PH.model.Report', - autoLoad: false, - alias: 'store.reports', - cycleId: null, - - proxy: { - type: 'rest', - api: { - read: '/report', - update: '/report', - delete: '/report', - create: '/report' - }, - reader: { - type: 'json' - }, - extraParams: { - cycleId: '0:0' - } - } -}); \ No newline at end of file diff --git a/src/webapp/app/app/store/Users.js b/src/webapp/app/app/store/Users.js deleted file mode 100644 index 00c2f0e..0000000 --- a/src/webapp/app/app/store/Users.js +++ /dev/null @@ -1,19 +0,0 @@ -Ext.define('PH.store.Users', { - extend: 'Ext.data.Store', - model: 'PH.model.User', - autoLoad: true, - alias: 'store.users', - - proxy: { - type: 'rest', - api: { - read: '/user', - update: '/user', - delete: '/user', - create: '/user/register' - }, - reader: { - type: 'json' - } - } -}); \ No newline at end of file diff --git a/src/webapp/app/app/utils/CommonUtils.js b/src/webapp/app/app/utils/CommonUtils.js deleted file mode 100644 index cfbe66e..0000000 --- a/src/webapp/app/app/utils/CommonUtils.js +++ /dev/null @@ -1,161 +0,0 @@ - -Ext.define ('PH.utils.CommonUtils', { - requires: [ - 'Ext.*', - 'Ext.LoadMask' - ], - - singleton: true, - - getLocaleString: function (group, value) { - var grp = GLocale[group] ? GLocale[group] : group; - if (grp) { - return grp[value] ? grp[value] : grp + '.' + value; - } - - return group + '.' + value; - }, - - /** - * Function refreshes any grid and after restores current selection based - * on value of the key field - * @param grid - grid to be refreshed - * @param params - additional parameters which use to reload store - * @param idFieldName - key field name - * @param selectedItemObj - * @param callbackFunc - * @param scope - * @param disableSelect - */ - refreshAnyGrid: function (grid, params, idFieldName, selectedItemObj, callbackFunc, scope, disableSelect) { - // get current selection - var selectedItem = selectedItemObj; - if (!selectedItem) - selectedItem = grid.getSelectionModel().getLastSelected(); - - grid.getSelectionModel().deselectAll(); - grid.getStore().sorters.clear(); - - grid.getStore().load({ - params: params, - async: false, - callback: function (records, operation, success) { - if (disableSelect !== true) { - if (selectedItem && idFieldName) { - // restore previously selected item - var record = grid.getStore().findRecord (idFieldName, selectedItem.get(idFieldName)); - if (record) - grid.getSelectionModel().select (record); - else { - if (!isNaN(selectedItem.index) && records && records.length >= selectedItem.index) - grid.getSelectionModel().select (records[selectedItem.index]); - else if (records && records.length > 0) - grid.getSelectionModel().select (records[0]); - } - } - else { - // select first record - if (records && records.length > 0) - grid.getSelectionModel().select (records[0]); - } - } - - if (callbackFunc) - callbackFunc.call (scope ? scope : this, records, operation, success); - }, - exception: function (proxy, response, operation) { - if (operation) { - Ext.Msg.alert('Error', operation.error); - } - else { - Ext.Msg.alert ('Error', 'Proxy error'); - } - } - }); - }, - - selectFirstElement: function( grid ){ - try{ - grid.getSelectionModel().select (0); - } - catch (e){}; - }, - - statusRenderer: function (value) { - return value; - }, - - showEditDialog: function (dialog, record, callback, scope, isNew) { - var win = Ext.create('Ext.window.Window', { - title: PH.utils.CommonUtils.getLocaleString ('titles', dialog), - layout: 'fit', - modal: true, - items: { - xtype: dialog - }, - buttons: [{ - text: PH.utils.CommonUtils.getLocaleString ('button', 'save'), - listeners: { - click: function () { - callback (win); - } - } - }, { - text: PH.utils.CommonUtils.getLocaleString ('button', 'cancel'), - listeners: { - click: function () { - win.close(); - } - } - }] - }); - - // load record data - if (!isNew) - win.down('form').loadRecord(record); - else { - var pass = win.down('form').getForm().findField("password"); - if (pass) - pass.hidden = false; - } - - win.on ('destroy', callback, scope); - win.show(); - }, - - createColumnFromModel: function (res, model, defaultWidth, maxCols, wordWrap) { - if (!defaultWidth) - defaultWidth = 200; - - var columns = []; - if (model.model) - model = model.model; - - Ext.each (model.getFields(), function(field) { - if (maxCols && columns.length === maxCols) - return true; - - if (field.name != 'id') { - var column = { - text: res.fields[field.name] ? res.fields[field.name] : field.name, - width: defaultWidth, - dataIndex: field.name, - stateId: field.name - }; - - if (field.type === 'date') { - column.renderer = Ext.util.Format.dateRenderer('d.m.Y H:i'); - } - else if (wordWrap) { - column.renderer = function (val) { - return '
' + val + '
'; - }; - } - - columns.push(column); - } - }); - - return columns; - } -}); \ No newline at end of file diff --git a/src/webapp/app/app/view/ContentPanel.js b/src/webapp/app/app/view/ContentPanel.js deleted file mode 100644 index 3571c3a..0000000 --- a/src/webapp/app/app/view/ContentPanel.js +++ /dev/null @@ -1,9 +0,0 @@ - -Ext.define('PH.view.ContentPanel', { - extend: 'Ext.panel.Panel', - xtype: 'contentPanel', - title: '', - autoScroll: true, - layout: 'fit' -}); - diff --git a/src/webapp/app/app/view/Navigation.js b/src/webapp/app/app/view/Navigation.js deleted file mode 100644 index fef9642..0000000 --- a/src/webapp/app/app/view/Navigation.js +++ /dev/null @@ -1,38 +0,0 @@ - -Ext.define('PH.view.Navigation', { - extend: 'Ext.tree.Panel', - xtype: 'navigation', - title: PH.utils.CommonUtils.getLocaleString('titles', 'navi'), - rootVisible: false, - store: 'Menu', - lines: true, - useArrows: true, - idField: 'id', - stateId : 'navi-tree', - stateful : true, - stateEvents: ['selectionchange', 'expand', 'itemexpand', 'collapse', 'itemcollapse'], - displayField: 'name', - - getState : function () { - var expandedNodes = []; - this.getRootNode().eachChild (function (child) { - if (child.isExpanded() === true) { - expandedNodes.push(child.getPath()); - } - }); - - return { - expanded: expandedNodes - }; - }, - applyState : function (state) { - var that = this; - this.on ('load', function () { - Ext.Array.forEach (state.expanded, function (path) { - that.expandPath (path, null, null, function (success, node) { - }); - }, that); - }); - } -}); - diff --git a/src/webapp/app/app/view/Viewport.js b/src/webapp/app/app/view/Viewport.js deleted file mode 100644 index 3e5c19b..0000000 --- a/src/webapp/app/app/view/Viewport.js +++ /dev/null @@ -1,40 +0,0 @@ - -Ext.define('PH.view.Viewport', { - extend: 'Ext.container.Viewport', - requires: [ - 'Ext.layout.container.Border', - 'PH.utils.CommonUtils', - 'PH.view.Navigation', - 'PH.view.ContentPanel', - 'PH.view.user.List', - 'Ext.tree.*', - 'PH.view.report.List', - 'PH.view.project.PrjList' - ], - layout: 'border', - items: [{ - region: 'north', - xtype: 'toolbar', - items: [{ - xtype: 'button', - text: 'Menu Button' - }, { - xtype: 'combo', - text: 'Right choise' - }, '->', { - xtype: 'splitbutton', - text : 'Split Button' - }] - }, { - region: 'west', - collapsible: true, - xtype: 'navigation', - title: 'Navigation', - width: 250, - split: true, - margin: '0 5 5 5' - }, { - region: 'center', - xtype: 'contentPanel' - }] -}); \ No newline at end of file diff --git a/src/webapp/app/app/view/project/CycleRecord.js b/src/webapp/app/app/view/project/CycleRecord.js deleted file mode 100644 index 6354a2a..0000000 --- a/src/webapp/app/app/view/project/CycleRecord.js +++ /dev/null @@ -1,40 +0,0 @@ -Ext.define('PH.view.project.CycleRecord', { - extend: 'Ext.form.Panel', - alias: 'widget.cycleEdit', - xtype: 'cycleEdit', - requires: [ - 'PH.store.Reports', - 'PH.utils.CommonUtils' - ], - store: 'reports', - defaultType: 'textfield', - items: [{ - xtype: 'fieldset', - items: [{ - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'pharmNet'), - xtype: 'textfield', - name: 'pharmNet' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'pharmacy'), - xtype: 'textfield', - name: 'pharmacy' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'agreements'), - xtype: 'textfield', - name: 'agreements' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'managerName'), - xtype: 'textfield', - name: 'managerName' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'managerPhone'), - xtype: 'textfield', - name: 'managerPhone' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'tradeRoomPhone'), - xtype: 'textfield', - name: 'tradeRoomPhone' - }] - }] -}); - diff --git a/src/webapp/app/app/view/project/PrjController.js b/src/webapp/app/app/view/project/PrjController.js deleted file mode 100644 index e826881..0000000 --- a/src/webapp/app/app/view/project/PrjController.js +++ /dev/null @@ -1,20 +0,0 @@ - -Ext.define("PH.view.project.PrjController", { - extend: 'Ext.app.ViewController', - alias: 'controller.projectList', - - onSelectProject: function (grid, selected, eOpts) { - var cycleGrid = this.lookupReference('cycleGrid'); - Ext.apply(store.getProxy().extraParams, { - prjId : selected[0].get('id'), - }); - store.load(); - }, - onSelectCycle: function (grid, selected, eOpts) { - -// this.lookupReference('prjCycle') - - } - -}); - diff --git a/src/webapp/app/app/view/project/PrjList.js b/src/webapp/app/app/view/project/PrjList.js deleted file mode 100644 index 6a2630a..0000000 --- a/src/webapp/app/app/view/project/PrjList.js +++ /dev/null @@ -1,50 +0,0 @@ -Ext.define('PH.view.project.PrjList', { - extend: 'Ext.panel.Panel', - alias: 'widget.projects', - reference: 'reportGrid', - requires: [ - 'Ext.grid.Panel', - 'Ext.layout.container.Border', - 'PH.store.Project', - 'PH.view.project.PrjController', - 'PH.view.project.PrjRecord', - 'PH.view.project.CycleRecord' - ], - controller: 'projectList', - layout: 'border', - items: [{ - region: 'center', - xtype: 'grid', - store: Ext.create('PH.store.Project'), - reference: 'projectGrid', - tbar: [{ - xtype: 'button', - text: PH.utils.CommonUtils.getLocaleString('button', 'add') - }], - listeners: { - selectionchange: 'onSelectProject' - } - }, { - region: 'south', - xtype: 'grid', - height: 200, - store: Ext.create('PH.store.PrjCycle'), - reference: 'cycleGrid', - columns: [{ - header: PH.utils.CommonUtils.getLocaleString('fields', 'name'), - dataIndex: 'name', - flex:1 - }, { - header: PH.utils.CommonUtils.getLocaleString('fields', 'startDate'), - dataIndex: 'startDate', - flex:1 - }], - tbar: [{ - xtype: 'button', - text: PH.utils.CommonUtils.getLocaleString('button', 'add') - }], - listeners: { - selectionchange: 'onSelectCycle ' - } - }] -}); \ No newline at end of file diff --git a/src/webapp/app/app/view/project/PrjRecord.js b/src/webapp/app/app/view/project/PrjRecord.js deleted file mode 100644 index db3d19b..0000000 --- a/src/webapp/app/app/view/project/PrjRecord.js +++ /dev/null @@ -1,40 +0,0 @@ -Ext.define('PH.view.project.PrjRecord', { - extend: 'Ext.form.Panel', - alias: 'widget.projectEdit', - xtype: 'projectEdit', - requires: [ - 'PH.store.Reports', - 'PH.utils.CommonUtils' - ], - store: 'reports', - defaultType: 'textfield', - items: [{ - xtype: 'fieldset', - items: [{ - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'pharmNet'), - xtype: 'textfield', - name: 'pharmNet' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'pharmacy'), - xtype: 'textfield', - name: 'pharmacy' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'agreements'), - xtype: 'textfield', - name: 'agreements' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'managerName'), - xtype: 'textfield', - name: 'managerName' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'managerPhone'), - xtype: 'textfield', - name: 'managerPhone' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'tradeRoomPhone'), - xtype: 'textfield', - name: 'tradeRoomPhone' - }] - }] -}); - diff --git a/src/webapp/app/app/view/report/List.js b/src/webapp/app/app/view/report/List.js deleted file mode 100644 index 533ca95..0000000 --- a/src/webapp/app/app/view/report/List.js +++ /dev/null @@ -1,68 +0,0 @@ -Ext.define('PH.view.report.List', { - extend: 'Ext.grid.Panel', - alias: 'widget.reports', - reference: 'reportGrid', - requires: [ - 'PH.store.Reports', - 'PH.store.Project', - 'PH.view.report.ListController', - 'PH.view.report.Record' - ], - controller: 'reportList', - columns: [ - {header: 'Name', dataIndex: 'name', flex:1}, - {header: 'Email', dataIndex: 'email', flex:1} - ], - tbar: [{ - xtype: 'label', - text: PH.utils.CommonUtils.getLocaleString('select', 'project') - }, { - xtype: 'combo', - name: 'project', - store: Ext.create ('PH.store.Project'), - editable: false, - displayField: 'name', - valueField: 'id', - listeners: { - select: 'selectProject' - }, - reference: 'project' - }, { - xtype: 'label', - text: PH.utils.CommonUtils.getLocaleString('select', 'cycle') - }, { - xtype: 'combo', - name: 'cycle', - store: Ext.create ('PH.store.PrjCycle'), - editable: false, - displayField: 'name', - valueField: 'id', - listeners: { - select: 'selectPrjCycle' - }, - reference: 'prjCycle' - }, { - xtype: 'button', - action: 'refresh', - text: PH.utils.CommonUtils.getLocaleString('button', 'refresh') - }, { - xtype: 'button', - action: 'add', - text: PH.utils.CommonUtils.getLocaleString('button', 'add') - }, { - xtype: 'button', - action: 'delete', - text: PH.utils.CommonUtils.getLocaleString('button', 'delete') - }], - initComponent: function () { - this.store = Ext.create('PH.store.Reports', { - storeId: 'reports' - }); - this.columns = [ - {header: 'Name', dataIndex: 'name', flex:1}, - {header: 'Email', dataIndex: 'email', flex:1} - ]; - - this.callParent(arguments); - } -}); \ No newline at end of file diff --git a/src/webapp/app/app/view/report/ListController.js b/src/webapp/app/app/view/report/ListController.js deleted file mode 100644 index 6c22594..0000000 --- a/src/webapp/app/app/view/report/ListController.js +++ /dev/null @@ -1,75 +0,0 @@ - -Ext.define("PH.view.report.ListController", { - extend: 'Ext.app.ViewController', - alias: 'controller.reportList', - - control: { - '#': { - itemdblclick: 'editReport' - }, - 'button[action=add]': { - click: 'createReport' - }, - 'button[action=refresh]': { - click: 'refreshReport' - }, - 'button[action=delete]': { - click: 'deleteReport' - }, - 'button[action=sync]': { - click: 'sync' - } - }, - selectProject: function (combo) { - var store = this.lookupReference('prjCycle').getStore(); - Ext.apply(store.getProxy().extraParams, { - prjId : combo.getSelectedRecord().get('id'), - }); - store.load(); - }, - selectPrjCycle: function (combo) { - // this.getView().getStore().load(); - var store = this.getView().getStore(); - Ext.apply(store.getProxy().extraParams, { - cycleId : combo.getSelectedRecord().get('id'), - }); - store.load(); - }, - editReport: function(grid, record) { - PH.utils.CommonUtils.showEditDialog ('editReport', record, function(win) { - var values = win.down('form').getValues(); - record.set(values); - grid.getStore().sync(); - win.close(); - }, this); - }, - createReport: function(button) { - var self = this; - PH.utils.CommonUtils.showEditDialog ('reportEdit', null, function(win) { - var values = win.down('form').getValues(); - var record = self.getView().getStore().add(values); - // special fix for ExtJS without this flag store noy send new added record to server - record[0].phantom = true; - // record[0].set(values); - // self.getView().getStore().commitChanges(); - - self.getView().getStore().sync({ - success: function(batch, opts) { - win.hide(); - }, - failure: function (batch, opts) { - } - }) - }, this, true); - }, - deleteReport: function (button) { - - }, - refreshReport: function (button) { - this.getView().getStore().load(); - }, - sync: function(button) { - this.getView().getStore().sync(); - } -}); - diff --git a/src/webapp/app/app/view/report/Record.js b/src/webapp/app/app/view/report/Record.js deleted file mode 100644 index 16a5f2b..0000000 --- a/src/webapp/app/app/view/report/Record.js +++ /dev/null @@ -1,160 +0,0 @@ - -Ext.define('PH.view.report.Record', { - extend: 'Ext.form.Panel', - alias: 'widget.reportEdit', - xtype: 'reportEdit', - requires: [ - 'PH.store.Reports', - 'PH.utils.CommonUtils' - ], - store: 'reports', - defaultType: 'textfield', - items: [{ - xtype: 'fieldset', - items: [{ - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'city'), - xtype: 'combobox', - triggerAction:'all', - typeAhead:true, - mode:'remote', - minChars:4, - hideTrigger:true, - store: null, - displayField: 'name', - valueField: 'name', - required: true, - name: 'city', - listeners: { - select: function (combo) { - var store = combo.up('form').getForm().findField("street").getStore(); - Ext.apply(store.getProxy().extraParams, { - cityId : combo.getSelectedRecord().get('id'), - }); - } - } - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'street'), - xtype: 'combobox', - triggerAction:'all', - typeAhead:true, - mode:'remote', - minChars:4, - hideTrigger:true, - store: null, - displayField: 'name', - valueField: 'name', - required: true, - name: 'street', - listeners: { - select: function (combo) { - var store = combo.up('form').getForm().findField("building").getStore(); - Ext.apply(store.getProxy().extraParams, { - streetId : combo.getSelectedRecord().get('id'), - }); - } - } - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'building'), - xtype: 'combobox', - triggerAction:'all', - typeAhead:true, - mode:'remote', - minChars: 1, - store: null, - hideTrigger:true, - displayField: 'name', - valueField: 'name', - required: true, - name: 'building' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'pharmNet'), - xtype: 'textfield', - name: 'pharmNet' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'pharmacy'), - xtype: 'textfield', - name: 'pharmacy' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'agreements'), - xtype: 'textfield', - name: 'agreements' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'managerName'), - xtype: 'textfield', - name: 'managerName' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'managerPhone'), - xtype: 'textfield', - name: 'managerPhone' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'tradeRoomPhone'), - xtype: 'textfield', - name: 'tradeRoomPhone' - }] - }], - - initComponent: function () { - this.callParent(arguments); - - var cityStore = Ext.create ('Ext.data.JsonPStore', { - fields: [ - 'id', 'name', 'name', 'type', 'zip', - 'contentType', 'parents' - ], - proxy: { - type: 'jsonp', - url: 'https://kladr-api.ru/api.php?contentType=city&limit=10', - reader: { - type: 'json', - rootProperty: 'result', - callbackKey: 'theCallbackFunction' - } - } - }); - - this.getForm().findField("city").setStore (cityStore) - - var streetStore = Ext.create ('Ext.data.JsonPStore', { - fields: [ - 'id', 'name', 'name', 'type', 'zip', - 'contentType', 'parents' - ], - proxy: { - type: 'jsonp', - url: 'https://kladr-api.ru/api.php?contentType=street&limit=10', - extraParams: { - cityId: 'none' - }, - reader: { - type: 'json', - rootProperty: 'result', - callbackKey: 'theCallbackFunction' - } - } - }); - - this.getForm().findField("street").setStore (streetStore) - - var buildingStore = Ext.create ('Ext.data.JsonPStore', { - fields: [ - 'id', 'name', 'name', 'type', 'zip', - 'contentType', 'parents' - ], - proxy: { - type: 'jsonp', - url: 'https://kladr-api.ru/api.php?contentType=building&limit=10', - extraParams: { - streetId: 'none' - }, - reader: { - type: 'json', - rootProperty: 'result', - callbackKey: 'theCallbackFunction' - } - } - }); - - this.getForm().findField("building").setStore (buildingStore) - } -}); - diff --git a/src/webapp/app/app/view/user/List.js b/src/webapp/app/app/view/user/List.js deleted file mode 100644 index 4b63824..0000000 --- a/src/webapp/app/app/view/user/List.js +++ /dev/null @@ -1,44 +0,0 @@ - -Ext.define('PH.view.user.List', { - extend: 'Ext.grid.Panel', - alias: 'widget.users', - - requires: [ - 'PH.store.Users', - 'PH.view.user.ListController', - 'PH.view.user.Record' - ], - controller: 'userList', - tbar: [{ - xtype: 'button', - action: 'refresh', - text: PH.utils.CommonUtils.getLocaleString('button', 'refresh') - }, { - xtype: 'button', - action: 'add', - text: PH.utils.CommonUtils.getLocaleString('button', 'add') - }, { - xtype: 'button', - action: 'delete', - text: PH.utils.CommonUtils.getLocaleString('button', 'delete') - }, { - xtype: 'button', - action: 'sync', - text: PH.utils.CommonUtils.getLocaleString('button', 'sync') - }], - columns: [ - {header: 'Name', dataIndex: 'name', flex:1}, - {header: 'Email', dataIndex: 'email', flex:1} - ], - initComponent: function () { - this.store = Ext.create('PH.store.Users', { - storeId: 'users' - }); - this.columns = [ - {header: 'Name', dataIndex: 'name', flex:1}, - {header: 'Email', dataIndex: 'email', flex:1} - ]; - - this.callParent(arguments); - } -}); \ No newline at end of file diff --git a/src/webapp/app/app/view/user/ListController.js b/src/webapp/app/app/view/user/ListController.js deleted file mode 100644 index 51c9d08..0000000 --- a/src/webapp/app/app/view/user/ListController.js +++ /dev/null @@ -1,86 +0,0 @@ - -Ext.define("PH.view.user.ListController", { - extend: 'Ext.app.ViewController', - alias: 'controller.userList', - - control: { - '#': { - itemdblclick: 'editUser' - }, - 'button[action=add]': { - click: 'createUser' - }, - 'button[action=refresh]': { - click: 'refreshUsers' - }, - 'button[action=delete]': { - click: 'deleteUser' - }, - 'button[action=sync]': { - click: 'sync' - } - }, - editUser: function(grid, record) { - PH.utils.CommonUtils.showEditDialog ('userEdit', record, function(win) { - var values = win.down('form').getValues(); - record.set(values); - grid.getStore().sync({ - success: function(batch, opts) { - win.hide(); - }, - failure: function (batch, opts) { - grid.getStore().load(); - - if (batch.hasException) { - for (var i in batch.exceptions) { - Ext.Msg.alert('Failure', batch.exceptions[i].getError()); - } - } - } - }); - win.hide(); - }, this); - }, - createUser: function() { - var self = this; - PH.utils.CommonUtils.showEditDialog ('userEdit', null, function(win) { - var values = win.down('form').getValues(); - var record = self.getView().getStore().add(values); - // special fix for ExtJS without this flag store noy send new added record to server - record[0].phantom = true; - // self.getView().getStore().commitChanges(); - - self.getView().getStore().sync({ - success: function(batch, opts) { - win.hide(); - }, - failure: function (batch, opts) { - grid.getStore().load(); - - if (batch.hasException) { - for (var i in batch.exceptions) { - Ext.Msg.alert('Failure', batch.exceptions[i].getError()); - } - } - } - }) - }, this, true); - }, - - deleteUser: function(button) { - // var win = button.up('window'), - // form = win.down('form'), - // record = form.getRecord(), - // values = form.getValues(); - // - // record.set(values); - // win.close(); - }, - refreshUsers: function(button) { - this.getView().getStore().load(); - }, - sync: function(button) { - this.getView().getStore().sync(); - } -}); - diff --git a/src/webapp/app/app/view/user/Record.js b/src/webapp/app/app/view/user/Record.js deleted file mode 100644 index 284bf00..0000000 --- a/src/webapp/app/app/view/user/Record.js +++ /dev/null @@ -1,47 +0,0 @@ - -Ext.define('PH.view.user.Record', { - extend: 'Ext.form.Panel', - alias: 'widget.userEdit', - xtype: 'userEdit', - requires: [ - 'PH.store.Users', - 'PH.utils.CommonUtils' - ], - store: 'users', - defaultType: 'textfield', - items: [{ - xtype: 'fieldset', - items: [{ - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'login'), - xtype: 'textfield', - name: 'login' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'password'), - xtype: 'textfield', - inputType: 'password', - name: 'password', - hidden: true - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'email'), - xtype: 'textfield', - name: 'email' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'firstName'), - xtype: 'textfield', - name: 'firstName' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'secondName'), - xtype: 'textfield', - name: 'secondName' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'manager'), - xtype: 'checkbox', - name: 'manager' - }, { - fieldLabel: PH.utils.CommonUtils.getLocaleString ('fields', 'activated'), - xtype: 'checkbox', - name: 'activated' - }] - }] -}); - diff --git a/src/webapp/app/data/menu.json b/src/webapp/app/data/menu.json deleted file mode 100644 index b756893..0000000 --- a/src/webapp/app/data/menu.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "menuItemList": [{ - "name": "security", - "role": "manager", - "leaf": false, - "menuItemList": [{ - "name": "users", - "module": "users", - "leaf": true - }, { - "name": "projects", - "module": "projects", - "leaf": true - } - ] - }, { - "name": "reports", - "leaf": true, - "module": "reports" - }] -} \ No newline at end of file diff --git a/src/webapp/app/simple.html b/src/webapp/app/simple.html deleted file mode 100644 index 6387786..0000000 --- a/src/webapp/app/simple.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - Pharmacy - - - - - - - - - - - - diff --git a/src/webapp/favicon.ico b/src/webapp/favicon.ico new file mode 100644 index 0000000..68e62ee Binary files /dev/null and b/src/webapp/favicon.ico differ diff --git a/src/webapp/get-images.php b/src/webapp/get-images.php deleted file mode 100644 index 67c6b44..0000000 --- a/src/webapp/get-images.php +++ /dev/null @@ -1,21 +0,0 @@ -[{ - "name": "Jamie Avins", - "color": "Blue", - "hex": "0000FF" -},{ - "name": "Ed Spencer", - "color": "Red", - "hex": "FF0000" -},{ - "name": "Jarred Nicholls", - "color": "Green", - "hex": "00FF00" -},{ - "name": "Aaron Conran", - "color": "Purple", - "hex": "800080" -},{ - "name": "Evan Trimboli", - "color": "Orange", - "hex": "FF8000" -}] diff --git a/src/webapp/index.html b/src/webapp/index.html index 8a6ec05..dae1735 100644 --- a/src/webapp/index.html +++ b/src/webapp/index.html @@ -1,64 +1,125 @@ - - - - - + + + + + - Account Manager +

akka-http-session example

- - +

+ Logged in as: +

+

+ Get login request status: +

- +
+

+ Login: +

+

+ Password: +

+ + +
- - +
+ +
- - - - - + $('#do_login').click(function(e) { + $.ajax(addCsrfHeader({ + url: '/api/do_login', + type: 'POST', + data: JSON.stringify({ + login: $('#login').val(), + pass: $('#password').val() + }), + contentType: "application/json; charset=utf-8", + dataType: "json", + success: getCurrentLogin + })); + e.preventDefault(); + return false; + }); -

ListView Example

+ $('#do_register').click(function(e) { + $.ajax(addCsrfHeader({ + url: '/api/do_register', + type: 'POST', + data: JSON.stringify({ + login: $('#login').val(), + password: $('#password').val(), + email: 'user@demo.com' + }), + contentType: "application/json; charset=utf-8", + dataType: "json", + success: getCurrentLogin + })); + e.preventDefault(); + return false; + }); -
- Select image to upload: - - -
+ $('#do_logout').click(function(e) { + $.ajax(addCsrfHeader({ + url: '/api/do_logout', + type: 'POST', + success: getCurrentLogin + })); + e.preventDefault(); + return false; + }); -

Ext 4 replaces Ext.ListView with the default Ext.grid.Panel.

-

Note that the js is not minified so it is readable. See Extjs docs.

- + function addCsrfHeader(opts) { + var token = Cookies.get('XSRF-TOKEN'); + if (token) { + console.log('Setting csrf token: ' + token); + opts['headers'] = { + 'X-XSRF-TOKEN': token + } + } else { + console.log('No csrf token'); + } + + return opts; + } + + diff --git a/src/webapp/jquery.min.js b/src/webapp/jquery.min.js new file mode 100644 index 0000000..7ac04f5 --- /dev/null +++ b/src/webapp/jquery.min.js @@ -0,0 +1,4 @@ +/*! jQuery v1.11.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.1",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="
",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h; + if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/\s*$/g,rb={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:k.htmlSerialize?[0,"",""]:[1,"X
","
"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?""!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("', - '{afterIFrameTpl}', - { - disableFormats: true - } - ], - stretchInputElFixed: true, - subTplInsertions: [ - /** - * @cfg {String/Array/Ext.XTemplate} beforeTextAreaTpl - * An optional string or `XTemplate` configuration to insert in the field markup - * before the textarea element. If an `XTemplate` is used, the component's - * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context. - */ - 'beforeTextAreaTpl', - /** - * @cfg {String/Array/Ext.XTemplate} afterTextAreaTpl - * An optional string or `XTemplate` configuration to insert in the field markup - * after the textarea element. If an `XTemplate` is used, the component's - * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context. - */ - 'afterTextAreaTpl', - /** - * @cfg {String/Array/Ext.XTemplate} beforeIFrameTpl - * An optional string or `XTemplate` configuration to insert in the field markup - * before the iframe element. If an `XTemplate` is used, the component's - * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context. - */ - 'beforeIFrameTpl', - /** - * @cfg {String/Array/Ext.XTemplate} afterIFrameTpl - * An optional string or `XTemplate` configuration to insert in the field markup - * after the iframe element. If an `XTemplate` is used, the component's - * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context. - */ - 'afterIFrameTpl', - /** - * @cfg {String/Array/Ext.XTemplate} iframeAttrTpl - * An optional string or `XTemplate` configuration to insert in the field markup - * inside the iframe element (as attributes). If an `XTemplate` is used, the component's - * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context. - */ - 'iframeAttrTpl', - // inherited - 'inputAttrTpl' - ], - /** - * @cfg {Boolean} enableFormat - * Enable the bold, italic and underline buttons - */ - enableFormat: true, - /** - * @cfg {Boolean} enableFontSize - * Enable the increase/decrease font size buttons - */ - enableFontSize: true, - /** - * @cfg {Boolean} enableColors - * Enable the fore/highlight color buttons - */ - enableColors: true, - /** - * @cfg {Boolean} enableAlignments - * Enable the left, center, right alignment buttons - */ - enableAlignments: true, - /** - * @cfg {Boolean} enableLists - * Enable the bullet and numbered list buttons. Not available in Safari 2. - */ - enableLists: true, - /** - * @cfg {Boolean} enableSourceEdit - * Enable the switch to source edit button. Not available in Safari 2. - */ - enableSourceEdit: true, - /** - * @cfg {Boolean} enableLinks - * Enable the create link button. Not available in Safari 2. - */ - enableLinks: true, - /** - * @cfg {Boolean} enableFont - * Enable font selection. Not available in Safari 2. - */ - enableFont: true, - // - /** - * @cfg {String} createLinkText - * The default text for the create link prompt - */ - createLinkText: 'Please enter the URL for the link:', - // - /** - * @cfg {String} [defaultLinkValue='http://'] - * The default value for the create link prompt - */ - defaultLinkValue: 'http:/' + '/', - /** - * @cfg {String[]} fontFamilies - * An array of available font families - */ - fontFamilies: [ - 'Arial', - 'Courier New', - 'Tahoma', - 'Times New Roman', - 'Verdana' - ], - /** - * @cfg {String} defaultValue - * A default value to be put into the editor to resolve focus issues. - * - * Defaults to (Non-breaking space) in Opera, - * (Zero-width space) in all other browsers. - */ - defaultValue: Ext.isOpera ? ' ' : '​', - /** - * @private - */ - extraFieldBodyCls: Ext.baseCSSPrefix + 'html-editor-wrap', - /** - * @cfg {String} defaultButtonUI - * A default {@link Ext.Component#ui ui} to use for the HtmlEditor's toolbar - * {@link Ext.button.Button Buttons} - */ - defaultButtonUI: 'default-toolbar', - /** - * @private - */ - initialized: false, - /** - * @private - */ - activated: false, - /** - * @private - */ - sourceEditMode: false, - /** - * @private - */ - iframePad: 3, - /** - * @private - */ - hideMode: 'offsets', - maskOnDisable: true, - containerElCls: Ext.baseCSSPrefix + 'html-editor-container', - // This will strip any number of single or double quotes (in any order) from a string at the anchors. - reStripQuotes: /^['"]*|['"]*$/g, - textAlignRE: /text-align:(.*?);/i, - safariNonsenseRE: /\sclass="(?:Apple-style-span|Apple-tab-span|khtml-block-placeholder)"/gi, - nonDigitsRE: /\D/g, - /** - * @event initialize - * Fires when the editor is fully initialized (including the iframe) - * @param {Ext.form.field.HtmlEditor} this - */ - /** - * @event activate - * Fires when the editor is first receives the focus. Any insertion must wait until after this event. - * @param {Ext.form.field.HtmlEditor} this - */ - /** - * @event beforesync - * Fires before the textarea is updated with content from the editor iframe. Return false to cancel the - * sync. - * @param {Ext.form.field.HtmlEditor} this - * @param {String} html - */ - /** - * @event beforepush - * Fires before the iframe editor is updated with content from the textarea. Return false to cancel the - * push. - * @param {Ext.form.field.HtmlEditor} this - * @param {String} html - */ - /** - * @event sync - * Fires when the textarea is updated with content from the editor iframe. - * @param {Ext.form.field.HtmlEditor} this - * @param {String} html - */ - /** - * @event push - * Fires when the iframe editor is updated with content from the textarea. - * @param {Ext.form.field.HtmlEditor} this - * @param {String} html - */ - /** - * @event editmodechange - * Fires when the editor switches edit modes - * @param {Ext.form.field.HtmlEditor} this - * @param {Boolean} sourceEdit True if source edit, false if standard editing. - */ - /** - * @private - */ - initComponent: function() { - var me = this; - me.items = [ - me.createToolbar(), - me.createInputCmp() - ]; - me.layout = { - type: 'vbox', - align: 'stretch' - }; - // No value set, we must report empty string - if (me.value == null) { - me.value = ''; - } - me.callParent(arguments); - me.initField(); - }, - createInputCmp: function() { - this.inputCmp = Ext.widget(this.getInputCmpCfg()); - return this.inputCmp; - }, - getInputCmpCfg: function() { - var me = this, - id = me.id + '-inputCmp', - data = { - id: id, - name: me.name, - textareaCls: me.textareaCls + ' ' + Ext.baseCSSPrefix + 'hidden', - value: me.value, - iframeName: Ext.id(), - iframeSrc: Ext.SSL_SECURE_URL, - iframeCls: Ext.baseCSSPrefix + 'htmleditor-iframe' - }; - me.getInsertionRenderData(data, me.subTplInsertions); - return { - flex: 1, - xtype: 'component', - tpl: me.getTpl('componentTpl'), - childEls: [ - 'iframeEl', - 'textareaEl' - ], - id: id, - cls: Ext.baseCSSPrefix + 'html-editor-input', - data: data - }; - }, - /** - * Called when the editor creates its toolbar. Override this method if you need to - * add custom toolbar buttons. - * @param {Ext.form.field.HtmlEditor} editor - * @protected - */ - createToolbar: function() { - this.toolbar = Ext.widget(this.getToolbarCfg()); - return this.toolbar; - }, - getToolbarCfg: function() { - var me = this, - items = [], - i, - tipsEnabled = Ext.quickTipsActive && Ext.tip.QuickTipManager.isEnabled(), - baseCSSPrefix = Ext.baseCSSPrefix, - fontSelectItem, undef; - function btn(id, toggle, handler) { - return { - itemId: id, - cls: baseCSSPrefix + 'btn-icon', - iconCls: baseCSSPrefix + 'edit-' + id, - enableToggle: toggle !== false, - scope: me, - handler: handler || me.relayBtnCmd, - clickEvent: 'mousedown', - tooltip: tipsEnabled ? me.buttonTips[id] || undef : undef, - overflowText: me.buttonTips[id].title || undef, - tabIndex: -1 - }; - } - if (me.enableFont && !Ext.isSafari2) { - fontSelectItem = Ext.widget('component', { - itemId: 'fontSelect', - renderTpl: [ - '' - ], - childEls: [ - 'selectEl' - ], - afterRender: function() { - me.fontSelect = this.selectEl; - Ext.Component.prototype.afterRender.apply(this, arguments); - }, - onDisable: function() { - var selectEl = this.selectEl; - if (selectEl) { - selectEl.dom.disabled = true; - } - Ext.Component.prototype.onDisable.apply(this, arguments); - }, - onEnable: function() { - var selectEl = this.selectEl; - if (selectEl) { - selectEl.dom.disabled = false; - } - Ext.Component.prototype.onEnable.apply(this, arguments); - }, - listeners: { - change: function() { - me.win.focus(); - me.relayCmd('fontName', me.fontSelect.dom.value); - me.deferFocus(); - }, - element: 'selectEl' - } - }); - items.push(fontSelectItem, '-'); - } - if (me.enableFormat) { - items.push(btn('bold'), btn('italic'), btn('underline')); - } - if (me.enableFontSize) { - items.push('-', btn('increasefontsize', false, me.adjustFont), btn('decreasefontsize', false, me.adjustFont)); - } - if (me.enableColors) { - items.push('-', { - itemId: 'forecolor', - cls: baseCSSPrefix + 'btn-icon', - iconCls: baseCSSPrefix + 'edit-forecolor', - overflowText: me.buttonTips.forecolor.title, - tooltip: tipsEnabled ? me.buttonTips.forecolor || undef : undef, - tabIndex: -1, - menu: Ext.widget('menu', { - plain: true, - items: [ - { - xtype: 'colorpicker', - allowReselect: true, - focus: Ext.emptyFn, - value: '000000', - plain: true, - clickEvent: 'mousedown', - handler: function(cp, color) { - me.relayCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#' + color : color); - this.up('menu').hide(); - } - } - ] - }) - }, { - itemId: 'backcolor', - cls: baseCSSPrefix + 'btn-icon', - iconCls: baseCSSPrefix + 'edit-backcolor', - overflowText: me.buttonTips.backcolor.title, - tooltip: tipsEnabled ? me.buttonTips.backcolor || undef : undef, - tabIndex: -1, - menu: Ext.widget('menu', { - plain: true, - items: [ - { - xtype: 'colorpicker', - focus: Ext.emptyFn, - value: 'FFFFFF', - plain: true, - allowReselect: true, - clickEvent: 'mousedown', - handler: function(cp, color) { - if (Ext.isGecko) { - me.execCmd('useCSS', false); - me.execCmd('hilitecolor', '#' + color); - me.execCmd('useCSS', true); - me.deferFocus(); - } else { - me.relayCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE || Ext.isOpera ? '#' + color : color); - } - this.up('menu').hide(); - } - } - ] - }) - }); - } - if (me.enableAlignments) { - items.push('-', btn('justifyleft'), btn('justifycenter'), btn('justifyright')); - } - if (!Ext.isSafari2) { - if (me.enableLinks) { - items.push('-', btn('createlink', false, me.createLink)); - } - if (me.enableLists) { - items.push('-', btn('insertorderedlist'), btn('insertunorderedlist')); - } - if (me.enableSourceEdit) { - items.push('-', btn('sourceedit', true, function() { - me.toggleSourceEdit(!me.sourceEditMode); - })); - } - } - // Everything starts disabled. - for (i = 0; i < items.length; i++) { - if (items[i].itemId !== 'sourceedit') { - items[i].disabled = true; - } - } - // build the toolbar - // Automatically rendered in Component.afterRender's renderChildren call - return { - xtype: 'toolbar', - defaultButtonUI: me.defaultButtonUI, - cls: Ext.baseCSSPrefix + 'html-editor-tb', - enableOverflow: true, - items: items, - // stop form submits - listeners: { - click: function(e) { - e.preventDefault(); - }, - element: 'el' - } - }; - }, - getMaskTarget: function() { - // Can't be the body td directly because of issues with absolute positioning - // inside td's in FF - return Ext.isGecko ? this.inputCmp.el : this.bodyEl; - }, - /** - * Sets the read only state of this field. - * @param {Boolean} readOnly Whether the field should be read only. - */ - setReadOnly: function(readOnly) { - var me = this, - textareaEl = me.textareaEl, - iframeEl = me.iframeEl, - body; - me.readOnly = readOnly; - if (textareaEl) { - textareaEl.dom.readOnly = readOnly; - } - if (me.initialized) { - body = me.getEditorBody(); - if (Ext.isIE) { - // Hide the iframe while setting contentEditable so it doesn't grab focus - iframeEl.setDisplayed(false); - body.contentEditable = !readOnly; - iframeEl.setDisplayed(true); - } else { - me.setDesignMode(!readOnly); - } - if (body) { - body.style.cursor = readOnly ? 'default' : 'text'; - } - me.disableItems(readOnly); - } - }, - /** - * Called when the editor initializes the iframe with HTML contents. Override this method if you - * want to change the initialization markup of the iframe (e.g. to add stylesheets). - * - * **Note:** IE8-Standards has unwanted scroller behavior, so the default meta tag forces IE7 compatibility. - * Also note that forcing IE7 mode works when the page is loaded normally, but if you are using IE's Web - * Developer Tools to manually set the document mode, that will take precedence and override what this - * code sets by default. This can be confusing when developing, but is not a user-facing issue. - * @protected - */ - getDocMarkup: function() { - var me = this, - h = me.iframeEl.getHeight() - me.iframePad * 2; - // - IE9+ require a strict doctype otherwise text outside visible area can't be selected. - // - Opera inserts

tags on Return key, so P margins must be removed to avoid double line-height. - // - On browsers other than IE, the font is not inherited by the IFRAME so it must be specified. - return Ext.String.format('' + '', me.iframePad, h, me.defaultFont); - }, - /** - * @private - */ - getEditorBody: function() { - var doc = this.getDoc(); - return doc.body || doc.documentElement; - }, - /** - * @private - */ - getDoc: function() { - return this.iframeEl.dom.contentDocument || this.getWin().document; - }, - /** - * @private - */ - getWin: function() { - // using window.frames[id] to access the the iframe's window object in FF creates - // a global variable with name == id in the global scope that references the iframe - // window. This is undesirable for unit testing because that global variable - // is readonly and cannot be deleted. To avoid this, we use contentWindow if it - // is available (and it is in all supported browsers at the time of this writing) - // and fall back to window.frames if contentWindow is not available. - return this.iframeEl.dom.contentWindow || window.frames[this.iframeEl.dom.name]; - }, - initDefaultFont: function() { - // It's not ideal to do this here since it's a write phase, but we need to know - // what the font used in the textarea is so that we can setup the appropriate font - // options in the select box. The select box will reflow once we populate it, so we want - // to do so before we layout the first time. - var me = this, - selIdx = 0, - fonts, font, select, option, i, len, lower; - if (!me.defaultFont) { - font = me.textareaEl.getStyle('font-family'); - font = Ext.String.capitalize(font.split(',')[0]); - fonts = Ext.Array.clone(me.fontFamilies); - Ext.Array.include(fonts, font); - fonts.sort(); - me.defaultFont = font; - select = me.down('#fontSelect').selectEl.dom; - for (i = 0 , len = fonts.length; i < len; ++i) { - font = fonts[i]; - lower = font.toLowerCase(); - option = new Option(font, lower); - if (font === me.defaultFont) { - selIdx = i; - } - option.style.fontFamily = lower; - if (Ext.isIE) { - select.add(option); - } else { - select.options.add(option); - } - } - // Old IE versions have a problem if we set the selected property - // in the loop, so set it after. - select.options[selIdx].selected = true; - } - }, - isEqual: function(value1, value2) { - return this.isEqualAsString(value1, value2); - }, - /** - * @private - */ - afterRender: function() { - var me = this, - inputCmp = me.inputCmp; - me.callParent(arguments); - me.iframeEl = inputCmp.iframeEl; - me.textareaEl = inputCmp.textareaEl; - // The input element is interrogated by the layout to extract height when labelAlign is 'top' - // It must be set, and then switched between the iframe and the textarea - me.inputEl = me.iframeEl; - if (me.enableFont) { - me.initDefaultFont(); - } - // Start polling for when the iframe document is ready to be manipulated - me.monitorTask = Ext.TaskManager.start({ - run: me.checkDesignMode, - scope: me, - interval: 100 - }); - }, - initFrameDoc: function() { - var me = this, - doc, task; - Ext.TaskManager.stop(me.monitorTask); - doc = me.getDoc(); - me.win = me.getWin(); - doc.open(); - doc.write(me.getDocMarkup()); - doc.close(); - task = { - // must defer to wait for browser to be ready - run: function() { - var doc = me.getDoc(); - if (doc.body || doc.readyState === 'complete') { - Ext.TaskManager.stop(task); - me.setDesignMode(true); - Ext.defer(me.initEditor, 10, me); - } - }, - interval: 10, - duration: 10000, - scope: me - }; - Ext.TaskManager.start(task); - }, - checkDesignMode: function() { - var me = this, - doc = me.getDoc(); - if (doc && (!doc.editorInitialized || me.getDesignMode() !== 'on')) { - me.initFrameDoc(); - } - }, - /** - * @private - * Sets current design mode. To enable, mode can be true or 'on', off otherwise - */ - setDesignMode: function(mode) { - var me = this, - doc = me.getDoc(); - if (doc) { - if (me.readOnly) { - mode = false; - } - doc.designMode = (/on|true/i).test(String(mode).toLowerCase()) ? 'on' : 'off'; - } - }, - /** - * @private - */ - getDesignMode: function() { - var doc = this.getDoc(); - return !doc ? '' : String(doc.designMode).toLowerCase(); - }, - disableItems: function(disabled) { - var items = this.getToolbar().items.items, - i, - iLen = items.length, - item; - for (i = 0; i < iLen; i++) { - item = items[i]; - if (item.getItemId() !== 'sourceedit') { - item.setDisabled(disabled); - } - } - }, - /** - * Toggles the editor between standard and source edit mode. - * @param {Boolean} [sourceEditMode] True for source edit, false for standard - */ - toggleSourceEdit: function(sourceEditMode) { - var me = this, - iframe = me.iframeEl, - textarea = me.textareaEl, - hiddenCls = Ext.baseCSSPrefix + 'hidden', - btn = me.getToolbar().getComponent('sourceedit'); - if (!Ext.isBoolean(sourceEditMode)) { - sourceEditMode = !me.sourceEditMode; - } - me.sourceEditMode = sourceEditMode; - if (btn.pressed !== sourceEditMode) { - btn.toggle(sourceEditMode); - } - if (sourceEditMode) { - me.disableItems(true); - me.syncValue(); - iframe.addCls(hiddenCls); - textarea.removeCls(hiddenCls); - textarea.dom.removeAttribute('tabIndex'); - textarea.focus(); - me.inputEl = textarea; - } else { - if (me.initialized) { - me.disableItems(me.readOnly); - } - me.pushValue(); - iframe.removeCls(hiddenCls); - textarea.addCls(hiddenCls); - textarea.dom.setAttribute('tabIndex', -1); - me.deferFocus(); - me.inputEl = iframe; - } - me.fireEvent('editmodechange', me, sourceEditMode); - me.updateLayout(); - }, - /** - * @private - */ - createLink: function() { - var url = prompt(this.createLinkText, this.defaultLinkValue); - if (url && url !== 'http:/' + '/') { - this.relayCmd('createlink', url); - } - }, - clearInvalid: Ext.emptyFn, - setValue: function(value) { - var me = this, - textarea = me.textareaEl; - if (value === null || value === undefined) { - value = ''; - } - // Only update the field if the value has changed - if (me.value !== value) { - if (textarea) { - textarea.dom.value = value; - } - me.pushValue(); - if (!me.rendered && me.inputCmp) { - me.inputCmp.data.value = value; - } - me.mixins.field.setValue.call(me, value); - } - return me; - }, - /** - * If you need/want custom HTML cleanup, this is the method you should override. - * @param {String} html The HTML to be cleaned - * @return {String} The cleaned HTML - * @protected - */ - cleanHtml: function(html) { - html = String(html); - if (Ext.isWebKit) { - // strip safari nonsense - html = html.replace(this.safariNonsenseRE, ''); - } - /* - * Neat little hack. Strips out all the non-digit characters from the default - * value and compares it to the character code of the first character in the string - * because it can cause encoding issues when posted to the server. We need the - * parseInt here because charCodeAt will return a number. - */ - if (html.charCodeAt(0) === parseInt(this.defaultValue.replace(this.nonDigitsRE, ''), 10)) { - html = html.substring(1); - } - return html; - }, - /** - * Syncs the contents of the editor iframe with the textarea. - * @protected - */ - syncValue: function() { - var me = this, - body, changed, html, bodyStyle, match, textElDom; - if (me.initialized) { - body = me.getEditorBody(); - html = body.innerHTML; - textElDom = me.textareaEl.dom; - if (Ext.isWebKit) { - bodyStyle = body.getAttribute('style'); - // Safari puts text-align styles on the body element! - match = bodyStyle.match(me.textAlignRE); - if (match && match[1]) { - html = '

' + html + '
'; - } - } - html = me.cleanHtml(html); - if (me.fireEvent('beforesync', me, html) !== false) { - // Gecko inserts single
tag when input is empty - // and user toggles source mode. See https://sencha.jira.com/browse/EXTJSIV-8542 - if (Ext.isGecko && textElDom.value === '' && html === '
') { - html = ''; - } - if (textElDom.value !== html) { - textElDom.value = html; - changed = true; - } - me.fireEvent('sync', me, html); - if (changed) { - // we have to guard this to avoid infinite recursion because getValue - // calls this method... - me.checkChange(); - } - } - } - }, - getValue: function() { - var me = this, - value; - if (!me.sourceEditMode) { - me.syncValue(); - } - value = me.rendered ? me.textareaEl.dom.value : me.value; - me.value = value; - return value; - }, - /** - * Pushes the value of the textarea into the iframe editor. - * @protected - */ - pushValue: function() { - var me = this, - v; - if (me.initialized) { - v = me.textareaEl.dom.value || ''; - if (!me.activated && v.length < 1) { - v = me.defaultValue; - } - if (me.fireEvent('beforepush', me, v) !== false) { - me.getEditorBody().innerHTML = v; - if (Ext.isGecko) { - // Gecko hack, see: https://bugzilla.mozilla.org/show_bug.cgi?id=232791#c8 - me.setDesignMode(false); - //toggle off first - me.setDesignMode(true); - } - me.fireEvent('push', me, v); - } - } - }, - focus: function(selectText, delay) { - var me = this, - value, focusEl; - if (delay) { - if (!me.focusTask) { - me.focusTask = new Ext.util.DelayedTask(me.focus); - } - me.focusTask.delay(Ext.isNumber(delay) ? delay : 10, null, me, [ - selectText, - false - ]); - } else { - if (selectText) { - if (me.textareaEl && me.textareaEl.dom) { - value = me.textareaEl.dom.value; - } - if (value && value.length) { - // Make sure there is content before calling SelectAll, otherwise the caret disappears. - me.execCmd('selectall', true); - } - } - focusEl = me.getFocusEl(); - if (focusEl && focusEl.focus) { - focusEl.focus(); - } - } - return me; - }, - /** - * @private - */ - initEditor: function() { - var me = this, - dbody, ss, doc, docEl, fn; - //Destroying the component during/before initEditor can cause issues. - if (me.destroying || me.destroyed) { - return; - } - dbody = me.getEditorBody(); - // IE has a null reference when it first comes online. - if (!dbody) { - setTimeout(function() { - me.initEditor(); - }, 10); - return; - } - ss = me.textareaEl.getStyle([ - 'font-size', - 'font-family', - 'background-image', - 'background-repeat', - 'background-color', - 'color' - ]); - ss['background-attachment'] = 'fixed'; - // w3c - dbody.bgProperties = 'fixed'; - // ie - Ext.DomHelper.applyStyles(dbody, ss); - doc = me.getDoc(); - docEl = Ext.get(doc); - if (docEl) { - try { - docEl.clearListeners(); - } catch (e) {} - /* - * We need to use createDelegate here, because when using buffer, the delayed task is added - * as a property to the function. When the listener is removed, the task is deleted from the function. - * Since onEditorEvent is shared on the prototype, if we have multiple html editors, the first time one of the editors - * is destroyed, it causes the fn to be deleted from the prototype, which causes errors. Essentially, we're just anonymizing the function. - */ - fn = me.onEditorEvent.bind(me); - docEl.on({ - mousedown: fn, - dblclick: fn, - click: fn, - keyup: fn, - delegated: false, - buffer: 100 - }); - // These events need to be relayed from the inner document (where they stop - // bubbling) up to the outer document. This has to be done at the DOM level so - // the event reaches listeners on elements like the document body. The effected - // mechanisms that depend on this bubbling behavior are listed to the right - // of the event. - fn = me.onRelayedEvent; - docEl.on({ - mousedown: fn, - // menu dismisal (MenuManager) and Window onMouseDown (toFront) - mousemove: fn, - // window resize drag detection - mouseup: fn, - // window resize termination - click: fn, - // not sure, but just to be safe - dblclick: fn, - // not sure again - delegated: false, - scope: me - }); - if (Ext.isGecko) { - docEl.on('keypress', me.applyCommand, me); - } - if (me.fixKeys) { - docEl.on('keydown', me.fixKeys, me, { - delegated: false - }); - } - if (me.fixKeysAfter) { - docEl.on('keyup', me.fixKeysAfter, me, { - delegated: false - }); - } - if (Ext.isIE9) { - Ext.get(doc.documentElement).on('focus', me.focus, me); - } - // In old IEs, clicking on a toolbar button shifts focus from iframe - // and it loses selection. To avoid this, we save current selection - // and restore it. - if (Ext.isIE8) { - docEl.on('focusout', function() { - me.savedSelection = doc.selection.type !== 'None' ? doc.selection.createRange() : null; - }, me); - docEl.on('focusin', function() { - if (me.savedSelection) { - me.savedSelection.select(); - } - }, me); - } - // We need to be sure we remove all our events from the iframe on unload or we're going to LEAK! - Ext.getWin().on('beforeunload', me.beforeDestroy, me); - doc.editorInitialized = true; - me.initialized = true; - me.pushValue(); - me.setReadOnly(me.readOnly); - me.fireEvent('initialize', me); - } - }, - /** - * @private - */ - beforeDestroy: function() { - var me = this, - monitorTask = me.monitorTask, - doc, prop; - if (monitorTask) { - Ext.TaskManager.stop(monitorTask); - } - if (me.rendered) { - Ext.getWin().un(me.beforeDestroy, me); - doc = me.getDoc(); - if (doc) { - // removeAll() doesn't currently know how to handle iframe document, - // so for now we have to wrap it in an Ext.Element, - // or else IE6/7 will leak big time when the page is refreshed. - // TODO: this may not be needed once we find a more permanent fix. - // see EXTJSIV-5891. - Ext.get(doc).destroy(); - if (doc.hasOwnProperty) { - for (prop in doc) { - try { - if (doc.hasOwnProperty(prop)) { - delete doc[prop]; - } - } catch (e) {} - } - } - } - // clearing certain props on document MAY throw in IE - delete me.iframeEl; - delete me.textareaEl; - delete me.toolbar; - delete me.inputCmp; - } - me.callParent(); - }, - /** - * @private - */ - onRelayedEvent: function(event) { - // relay event from the iframe's document to the document that owns the iframe... - var iframeEl = this.iframeEl, - iframeXY = Ext.fly(iframeEl).getTrueXY(), - originalEventXY = event.getXY(), - eventXY = event.getXY(); - // the event from the inner document has XY relative to that document's origin, - // so adjust it to use the origin of the iframe in the outer document: - event.xy = [ - iframeXY[0] + eventXY[0], - iframeXY[1] + eventXY[1] - ]; - event.injectEvent(iframeEl); - // blame the iframe for the event... - event.xy = originalEventXY; - }, - // restore the original XY (just for safety) - /** - * @private - */ - onFirstFocus: function() { - var me = this, - selection, range; - me.activated = true; - me.disableItems(me.readOnly); - if (Ext.isGecko) { - // prevent silly gecko errors - me.win.focus(); - selection = me.win.getSelection(); - // If the editor contains a
tag, clicking on the editor after the text where - // the
broke the line will produce nodeType === 1 (the body tag). - // It's better to check the length of the selection.focusNode's content. - // - // If htmleditor.value = ' ' (note the space) - // 1. nodeType === 1 - // 2. nodeName === 'BODY' - // 3. selection.focusNode.textContent.length === 1 - // - // If htmleditor.value = '' (no chars) nodeType === 3 && nodeName === '#text' - // 1. nodeType === 3 - // 2. nodeName === '#text' - // 3. selection.focusNode.textContent.length === 1 (yes, that's right, 1) - // - // The editor inserts Unicode code point 8203, a zero-width space when - // htmleditor.value === '' (call selection.focusNode.textContent.charCodeAt(0)) - // http://www.fileformat.info/info/unicode/char/200b/index.htm - // So, test with framework method to normalize. - if (selection.focusNode && !me.getValue().length) { - range = selection.getRangeAt(0); - range.selectNodeContents(me.getEditorBody()); - range.collapse(true); - me.deferFocus(); - } - try { - me.execCmd('useCSS', true); - me.execCmd('styleWithCSS', false); - } catch (e) {} - } - // ignore (why?) - me.fireEvent('activate', me); - }, - /** - * @private - */ - adjustFont: function(btn) { - var adjust = btn.getItemId() === 'increasefontsize' ? 1 : -1, - size = this.getDoc().queryCommandValue('FontSize') || '2', - isPxSize = Ext.isString(size) && size.indexOf('px') !== -1, - isSafari; - size = parseInt(size, 10); - if (isPxSize) { - // Safari 3 values - // 1 = 10px, 2 = 13px, 3 = 16px, 4 = 18px, 5 = 24px, 6 = 32px - if (size <= 10) { - size = 1 + adjust; - } else if (size <= 13) { - size = 2 + adjust; - } else if (size <= 16) { - size = 3 + adjust; - } else if (size <= 18) { - size = 4 + adjust; - } else if (size <= 24) { - size = 5 + adjust; - } else { - size = 6 + adjust; - } - size = Ext.Number.constrain(size, 1, 6); - } else { - isSafari = Ext.isSafari; - if (isSafari) { - // safari - adjust *= 2; - } - size = Math.max(1, size + adjust) + (isSafari ? 'px' : 0); - } - this.relayCmd('FontSize', size); - }, - /** - * @private - */ - onEditorEvent: function() { - this.updateToolbar(); - }, - /** - * Triggers a toolbar update by reading the markup state of the current selection in the editor. - * @protected - */ - updateToolbar: function() { - var me = this, - i, l, btns, doc, name, queriedName, fontSelect, toolbarSubmenus; - if (me.readOnly) { - return; - } - if (!me.activated) { - me.onFirstFocus(); - return; - } - btns = me.getToolbar().items.map; - doc = me.getDoc(); - if (me.enableFont && !Ext.isSafari2) { - // When querying the fontName, Chrome may return an Array of font names - // with those containing spaces being placed between single-quotes. - queriedName = doc.queryCommandValue('fontName'); - name = (queriedName ? queriedName.split(",")[0].replace(me.reStripQuotes, '') : me.defaultFont).toLowerCase(); - fontSelect = me.fontSelect.dom; - if (name !== fontSelect.value || name !== queriedName) { - fontSelect.value = name; - } - } - function updateButtons() { - var state; - for (i = 0 , l = arguments.length , name; i < l; i++) { - name = arguments[i]; - // Firefox 18+ sometimes throws NS_ERROR_INVALID_POINTER exception - // See https://sencha.jira.com/browse/EXTJSIV-9766 - try { - state = doc.queryCommandState(name); - } catch (e) { - state = false; - } - btns[name].toggle(state); - } - } - if (me.enableFormat) { - updateButtons('bold', 'italic', 'underline'); - } - if (me.enableAlignments) { - updateButtons('justifyleft', 'justifycenter', 'justifyright'); - } - if (!Ext.isSafari2 && me.enableLists) { - updateButtons('insertorderedlist', 'insertunorderedlist'); - } - // Ensure any of our toolbar's owned menus are hidden. - // The overflow menu must control itself. - toolbarSubmenus = me.toolbar.query('menu'); - for (i = 0; i < toolbarSubmenus.length; i++) { - toolbarSubmenus[i].hide(); - } - me.syncValue(); - }, - /** - * @private - */ - relayBtnCmd: function(btn) { - this.relayCmd(btn.getItemId()); - }, - /** - * Executes a Midas editor command on the editor document and performs necessary focus and toolbar updates. - * **This should only be called after the editor is initialized.** - * @param {String} cmd The Midas command - * @param {String/Boolean} [value=null] The value to pass to the command - */ - relayCmd: function(cmd, value) { - Ext.defer(function() { - var me = this; - if (!this.destroyed) { - me.win.focus(); - me.execCmd(cmd, value); - me.updateToolbar(); - } - }, 10, this); - }, - /** - * Executes a Midas editor command directly on the editor document. For visual commands, you should use - * {@link #relayCmd} instead. **This should only be called after the editor is initialized.** - * @param {String} cmd The Midas command - * @param {String/Boolean} [value=null] The value to pass to the command - */ - execCmd: function(cmd, value) { - var me = this, - doc = me.getDoc(); - doc.execCommand(cmd, false, (value === undefined ? null : value)); - me.syncValue(); - }, - /** - * @private - */ - applyCommand: function(e) { - if (e.ctrlKey) { - var me = this, - c = e.getCharCode(), - cmd; - if (c > 0) { - c = String.fromCharCode(c); - switch (c) { - case 'b': - cmd = 'bold'; - break; - case 'i': - cmd = 'italic'; - break; - case 'u': - cmd = 'underline'; - break; - } - if (cmd) { - me.win.focus(); - me.execCmd(cmd); - me.deferFocus(); - e.preventDefault(); - } - } - } - }, - /** - * Inserts the passed text at the current cursor position. - * __Note:__ the editor must be initialized and activated to insert text. - * @param {String} text - */ - insertAtCursor: function(text) { - // adapted from http://stackoverflow.com/questions/6690752/insert-html-at-caret-in-a-contenteditable-div/6691294#6691294 - var me = this, - win = me.getWin(), - doc = me.getDoc(), - sel, range, el, frag, node, lastNode, firstNode; - if (me.activated) { - win.focus(); - if (win.getSelection) { - sel = win.getSelection(); - if (sel.getRangeAt && sel.rangeCount) { - range = sel.getRangeAt(0); - range.deleteContents(); - // Range.createContextualFragment() would be useful here but is - // only relatively recently standardized and is not supported in - // some browsers (IE9, for one) - el = doc.createElement("div"); - el.innerHTML = text; - frag = doc.createDocumentFragment(); - while ((node = el.firstChild)) { - lastNode = frag.appendChild(node); - } - firstNode = frag.firstChild; - range.insertNode(frag); - // Preserve the selection - if (lastNode) { - range = range.cloneRange(); - range.setStartAfter(lastNode); - range.collapse(true); - sel.removeAllRanges(); - sel.addRange(range); - } - } - } else if (doc.selection && sel.type !== 'Control') { - sel = doc.selection; - range = sel.createRange(); - range.collapse(true); - sel.createRange().pasteHTML(text); - } - me.deferFocus(); - } - }, - /** - * @private - * Load time branching for fastest keydown performance. - */ - fixKeys: (function() { - var tag; - if (Ext.isIE10m) { - return function(e) { - var me = this, - k = e.getKey(), - doc = me.getDoc(), - readOnly = me.readOnly, - range, target; - if (k === e.TAB) { - e.stopEvent(); - // TODO: add tab support for IE 11. - if (!readOnly) { - range = doc.selection.createRange(); - if (range) { - if (range.collapse) { - range.collapse(true); - range.pasteHTML('    '); - } - me.deferFocus(); - } - } - } - }; - } - if (Ext.isOpera) { - return function(e) { - var me = this, - k = e.getKey(), - readOnly = me.readOnly; - if (k === e.TAB) { - e.stopEvent(); - if (!readOnly) { - me.win.focus(); - me.execCmd('InsertHTML', '    '); - me.deferFocus(); - } - } - }; - } - // Not needed, so null. - return null; - }()), - /** - * @private - */ - fixKeysAfter: (function() { - if (Ext.isIE) { - return function(e) { - var me = this, - k = e.getKey(), - doc = me.getDoc(), - readOnly = me.readOnly, - innerHTML; - if (!readOnly && (k === e.BACKSPACE || k === e.DELETE)) { - innerHTML = doc.body.innerHTML; - // If HtmlEditor had some input and user cleared it, IE inserts

 

- // which makes an impression that there is still some text, and creeps - // into source mode when toggled. We don't want this. - // - // See https://sencha.jira.com/browse/EXTJSIV-8542 - // - // N.B. There is **small** chance that user could go to source mode, - // type '

 

', switch back to visual mode, type something else - // and then clear it -- the code below would clear the

tag as well, - // which could be considered a bug. However I see no way to distinguish - // between offending markup being entered manually and generated by IE, - // so this can be considered a nasty corner case. - // - if (innerHTML === '

 

' || innerHTML === '

 

') { - doc.body.innerHTML = ''; - } - } - }; - } - return null; - }()), - /** - * Returns the editor's toolbar. **This is only available after the editor has been rendered.** - * @return {Ext.toolbar.Toolbar} - */ - getToolbar: function() { - return this.toolbar; - }, - // - /** - * @property {Object} buttonTips - * Object collection of toolbar tooltips for the buttons in the editor. The key is the command id associated with - * that button and the value is a valid QuickTips object. For example: - * - * { - * bold: { - * title: 'Bold (Ctrl+B)', - * text: 'Make the selected text bold.', - * cls: 'x-html-editor-tip' - * }, - * italic: { - * title: 'Italic (Ctrl+I)', - * text: 'Make the selected text italic.', - * cls: 'x-html-editor-tip' - * } - * // ... - * } - */ - buttonTips: { - bold: { - title: 'Bold (Ctrl+B)', - text: 'Make the selected text bold.', - cls: Ext.baseCSSPrefix + 'html-editor-tip' - }, - italic: { - title: 'Italic (Ctrl+I)', - text: 'Make the selected text italic.', - cls: Ext.baseCSSPrefix + 'html-editor-tip' - }, - underline: { - title: 'Underline (Ctrl+U)', - text: 'Underline the selected text.', - cls: Ext.baseCSSPrefix + 'html-editor-tip' - }, - increasefontsize: { - title: 'Grow Text', - text: 'Increase the font size.', - cls: Ext.baseCSSPrefix + 'html-editor-tip' - }, - decreasefontsize: { - title: 'Shrink Text', - text: 'Decrease the font size.', - cls: Ext.baseCSSPrefix + 'html-editor-tip' - }, - backcolor: { - title: 'Text Highlight Color', - text: 'Change the background color of the selected text.', - cls: Ext.baseCSSPrefix + 'html-editor-tip' - }, - forecolor: { - title: 'Font Color', - text: 'Change the color of the selected text.', - cls: Ext.baseCSSPrefix + 'html-editor-tip' - }, - justifyleft: { - title: 'Align Text Left', - text: 'Align text to the left.', - cls: Ext.baseCSSPrefix + 'html-editor-tip' - }, - justifycenter: { - title: 'Center Text', - text: 'Center text in the editor.', - cls: Ext.baseCSSPrefix + 'html-editor-tip' - }, - justifyright: { - title: 'Align Text Right', - text: 'Align text to the right.', - cls: Ext.baseCSSPrefix + 'html-editor-tip' - }, - insertunorderedlist: { - title: 'Bullet List', - text: 'Start a bulleted list.', - cls: Ext.baseCSSPrefix + 'html-editor-tip' - }, - insertorderedlist: { - title: 'Numbered List', - text: 'Start a numbered list.', - cls: Ext.baseCSSPrefix + 'html-editor-tip' - }, - createlink: { - title: 'Hyperlink', - text: 'Make the selected text a hyperlink.', - cls: Ext.baseCSSPrefix + 'html-editor-tip' - }, - sourceedit: { - title: 'Source Edit', - text: 'Switch to source editing mode.', - cls: Ext.baseCSSPrefix + 'html-editor-tip' - } - }, - // - // hide stuff that is not compatible - /** - * @event blur - * @private - */ - /** - * @event focus - * @private - */ - /** - * @event specialkey - * @private - */ - /** - * @cfg {String} fieldCls - * @private - */ - /** - * @cfg {String} focusCls - * @private - */ - /** - * @cfg {String} autoCreate - * @private - */ - /** - * @cfg {String} inputType - * @private - */ - /** - * @cfg {String} invalidCls - * @private - */ - /** - * @cfg {String} invalidText - * @private - */ - /** - * @cfg {Boolean} allowDomMove - * @private - */ - /** - * @cfg {String} readOnly - * @private - */ - /** - * @cfg {String} tabIndex - * @private - */ - /** - * @method validate - * @private - */ - privates: { - deferFocus: function() { - this.focus(false, true); - }, - getFocusEl: function() { - return this.sourceEditMode ? this.textareaEl : this.iframeEl; - } - } -}); - -/** - * `tagfield` provides a combobox that removes the hassle of dealing with long and unruly select - * options. The selected list is visually maintained in the value display area instead of - * within the picker itself. Users may easily add or remove `tags` from the - * display value area. - * - * @example - * var shows = Ext.create('Ext.data.Store', { - * fields: ['id','show'], - * data: [ - * {id: 0, show: 'Battlestar Galactica'}, - * {id: 1, show: 'Doctor Who'}, - * {id: 2, show: 'Farscape'}, - * {id: 3, show: 'Firefly'}, - * {id: 4, show: 'Star Trek'}, - * {id: 5, show: 'Star Wars: Christmas Special'} - * ] - * }); - * - * Ext.create('Ext.form.Panel', { - * renderTo: Ext.getBody(), - * title: 'Sci-Fi Television', - * height: 200, - * width: 500, - * items: [{ - * xtype: 'tagfield', - * fieldLabel: 'Select a Show', - * store: shows, - * displayField: 'show', - * valueField: 'id', - * queryMode: 'local', - * filterPickList: true - * }] - * }); - * - * ### History - * - * Inspired by the [SuperBoxSelect component for ExtJS 3](http://technomedia.co.uk/SuperBoxSelect/examples3.html), - * which in turn was inspired by the [BoxSelect component for ExtJS 2](http://efattal.fr/en/extjs/extuxboxselect/). - * - * Various contributions and suggestions made by many members of the ExtJS community which can be seen - * in the [user extension forum post](http://www.sencha.com/forum/showthread.php?134751-Ext.ux.form.field.BoxSelect). - * - * By: kvee_iv http://www.sencha.com/forum/member.php?29437-kveeiv - */ -Ext.define('Ext.form.field.Tag', { - extend: 'Ext.form.field.ComboBox', - requires: [ - 'Ext.selection.Model', - 'Ext.data.Store', - 'Ext.data.ChainedStore' - ], - xtype: 'tagfield', - noWrap: false, - /** - * @cfg allowOnlyWhitespace - * @hide - * Currently unsupported since the value of a tagfield is an array of values and shouldn't ever be a string. - */ - /** - * @cfg {String} valueParam - * The name of the parameter used to load unknown records into the store. If left unspecified, {@link #valueField} - * will be used. - */ - /** - * @cfg {Boolean} multiSelect - * If set to `true`, allows the combo field to hold more than one value at a time, and allows selecting multiple - * items from the dropdown list. The combo's text field will show all selected values using the template - * defined by {@link #labelTpl}. - * - */ - multiSelect: true, - /** - * @cfg {String} delimiter - * The character(s) used to separate new values to be added when {@link #createNewOnEnter} - * or {@link #createNewOnBlur} are set. - * `{@link #multiSelect} = true`. - */ - delimiter: ',', - /** - * @cfg {String/Ext.XTemplate} labelTpl - * The {@link Ext.XTemplate XTemplate} to use for the inner - * markup of the labeled items. Defaults to the configured {@link #displayField} - */ - /** - * @cfg {String/Ext.XTemplate} tipTpl - * The {@link Ext.XTemplate XTemplate} to use for the tip of the labeled items. - * - * @since 5.1.1 - */ - tipTpl: undefined, - /** - * @cfg - * @inheritdoc - * - * When {@link #forceSelection} is `false`, new records can be created by the user as they - * are typed. These records are **not** added to the combo's store. Multiple new values - * may be added by separating them with the {@link #delimiter}, and can be further configured using the - * {@link #createNewOnEnter} and {@link #createNewOnBlur} configuration options. - * - * This functionality is primarily useful for things such as an email address. - */ - forceSelection: true, - /** - * @cfg {Boolean} createNewOnEnter - * Has no effect if {@link #forceSelection} is `true`. - * - * With this set to `true`, the creation described in - * {@link #forceSelection} will also be triggered by the 'enter' key. - */ - createNewOnEnter: false, - /** - * @cfg {Boolean} createNewOnBlur - * Has no effect if {@link #forceSelection} is `true`. - * - * With this set to `true`, the creation described in - * {@link #forceSelection} will also be triggered when the field loses focus. - * - * Please note that this behavior is also affected by the configuration options - * {@link #autoSelect} and {@link #selectOnTab}. If those are true and an existing - * item would have been selected as a result, the partial text the user has entered will - * be discarded and the existing item will be added to the selection. - */ - createNewOnBlur: false, - /** - * @cfg {Boolean} encodeSubmitValue - * Has no effect if {@link #multiSelect} is `false`. - * - * Controls the formatting of the form submit value of the field as returned by {@link #getSubmitValue} - * - * - `true` for the field value to submit as a json encoded array in a single GET/POST variable - * - `false` for the field to submit as an array of GET/POST variables - */ - encodeSubmitValue: false, - /** - * @cfg {Boolean} triggerOnClick - * `true` to activate the trigger when clicking in empty space in the field. Note that the - * subsequent behavior of this is controlled by the field's {@link #triggerAction}. - * This behavior is similar to that of a basic ComboBox with {@link #editable} `false`. - */ - triggerOnClick: true, - /** - * @cfg {Boolean} stacked - * - `true` to have each selected value fill to the width of the form field - * - `false to have each selected value size to its displayed contents - */ - stacked: false, - /** - * @cfg {Boolean} filterPickList - * True to hide the currently selected values from the drop down list. - * - * - `true` to hide currently selected values from the drop down pick list - * - `false` to keep the item in the pick list as a selected item - */ - filterPickList: false, - /** - * @cfg {Boolean} - * - * `true` if this field should automatically grow and shrink vertically to its content. - * Note that this overrides the natural trigger grow functionality, which is used to size - * the field horizontally. - */ - grow: true, - /** - * @cfg {Number/Boolean} - * Has no effect if {@link #grow} is `false` - * - * The minimum height to allow when {@link #grow} is `true`, or `false` to allow for - * natural vertical growth based on the current selected values. See also {@link #growMax}. - */ - growMin: false, - /** - * @cfg {Number/Boolean} - * Has no effect if {@link #grow} is `false` - * - * The maximum height to allow when {@link #grow} is `true`, or `false` to allow for - * natural vertical growth based on the current selected values. See also {@link #growMin}. - */ - growMax: false, - /** - * @cfg - * @inheritdoc - */ - selectOnFocus: true, - /** - * @cfg growAppend - * @hide - * Currently unsupported since this is used for horizontal growth and this component - * only supports vertical growth. - */ - /** - * @cfg growToLongestValue - * @hide - * Currently unsupported since this is used for horizontal growth and this component - * only supports vertical growth. - */ - /** - * @event autosize - * Fires when the **{@link #autoSize}** function is triggered and the field is resized according to the - * {@link #grow}/{@link #growMin}/{@link #growMax} configs as a result. This event provides a hook for the - * developer to apply additional logic at runtime to resize the field if needed. - * @param {Ext.form.field.Tag} this This field - * @param {Number} height The new field height - */ - /** - * @private - * @cfg - */ - fieldSubTpl: [ - '
', - '
    ', - '
  • ', - '
    {emptyText}
    ', - 'name="{name}" ', - ' value="{[Ext.util.Format.htmlEncode(values.value)]}"', - 'size="{size}" ', - 'tabindex="{tabIdx}" ', - ' disabled="disabled"', - 'class="' + Ext.baseCSSPrefix + 'tagfield-input-field {inputElCls}" autocomplete="off">', - '
  • ', - '
', - '
', - { - disableFormats: true - } - ], - extraFieldBodyCls: Ext.baseCSSPrefix + 'tagfield-body', - /** - * @private - */ - childEls: [ - 'listWrapper', - 'itemList', - 'inputEl', - 'inputElCt', - 'emptyEl' - ], - /** - * @private - */ - emptyInputCls: Ext.baseCSSPrefix + 'tagfield-emptyinput', - /** - * @private - */ - clearValueOnEmpty: false, - tagItemCls: Ext.baseCSSPrefix + 'tagfield-item', - tagItemTextCls: Ext.baseCSSPrefix + 'tagfield-item-text', - tagItemCloseCls: Ext.baseCSSPrefix + 'tagfield-item-close', - tagItemSelector: '.' + Ext.baseCSSPrefix + 'tagfield-item', - tagItemCloseSelector: '.' + Ext.baseCSSPrefix + 'tagfield-item-close', - tagSelectedCls: Ext.baseCSSPrefix + 'tagfield-item-selected', - initComponent: function() { - var me = this, - typeAhead = me.typeAhead, - delimiter = me.delimiter; - if (typeAhead && !me.editable) { - Ext.raise('If typeAhead is enabled the combo must be editable: true -- please change one of those settings.'); - } - // Allow unmatched textual values to be converted into new value records. - if (me.createNewOnEnter || me.createNewOnBlur) { - me.forceSelection = false; - } - me.typeAhead = false; - if (me.value == null) { - me.value = []; - } - // This is the selection model for selecting tags in the tag list. NOT the dropdown BoundList. - // Create the selModel before calling parent, we need it to be available - // when we bind the store. - me.selectionModel = new Ext.selection.Model({ - mode: 'MULTI', - onSelectChange: function(record, isSelected, suppressEvent, commitFn) { - commitFn(); - }, - // Relay these selection events passing the field instead of exposing the underlying selection model - listeners: { - scope: me, - selectionchange: me.onSelectionChange, - focuschange: me.onFocusChange - } - }); - me.callParent(); - me.typeAhead = typeAhead; - if (delimiter && me.multiSelect) { - me.delimiterRegexp = new RegExp(Ext.String.escapeRegex(delimiter)); - } - }, - initEvents: function() { - var me = this, - inputEl = me.inputEl; - me.callParent(arguments); - if (!me.enableKeyEvents) { - inputEl.on('keydown', me.onKeyDown, me); - inputEl.on('keyup', me.onKeyUp, me); - } - me.listWrapper.on({ - scope: me, - click: me.onItemListClick, - mousedown: me.onItemMouseDown - }); - }, - isValid: function() { - var me = this, - disabled = me.disabled, - validate = me.forceValidation || !disabled; - return validate ? me.validateValue(me.getValue()) : disabled; - }, - onBindStore: function(store) { - var me = this; - me.callParent([ - store - ]); - if (store) { - // We collect picked records in a value store so that a selection model can track selection - me.valueStore = new Ext.data.Store({ - model: store.getModel(), - // We may have the empty store here, so just ignore empty models - useModelWarning: false - }); - me.selectionModel.bindStore(me.valueStore); - // Picked records disappear from the BoundList - if (me.filterPickList) { - me.listFilter = new Ext.util.Filter({ - scope: me, - filterFn: me.filterPicked - }); - me.changingFilters = true; - store.filter(me.listFilter); - me.changingFilters = false; - } - } - }, - filterPicked: function(rec) { - return !this.valueCollection.contains(rec); - }, - onUnbindStore: function(store) { - var me = this, - valueStore = me.valueStore, - picker = me.picker; - if (picker) { - picker.bindStore(null); - } - if (valueStore) { - valueStore.destroy(); - me.valueStore = null; - } - if (me.filterPickList && !store.destroyed) { - me.changingFilters = true; - store.removeFilter(me.listFilter); - me.changingFilters = false; - } - me.callParent(arguments); - }, - onValueCollectionEndUpdate: function() { - var me = this, - pickedRecords = me.valueCollection.items, - valueStore = me.valueStore; - if (me.isSelectionUpdating()) { - return; - } - // Ensure the source store is filtered down - if (me.filterPickList) { - me.changingFilters = true; - me.store.filter(me.listFilter); - me.changingFilters = false; - } - me.callParent(); - Ext.suspendLayouts(); - if (valueStore) { - valueStore.suspendEvents(); - valueStore.loadRecords(pickedRecords); - valueStore.resumeEvents(); - } - Ext.resumeLayouts(true); - me.alignPicker(); - }, - checkValueOnDataChange: Ext.emptyFn, - onSelectionChange: function(selModel, selectedRecs) { - this.applyMultiselectItemMarkup(); - this.fireEvent('valueselectionchange', this, selectedRecs); - }, - onFocusChange: function(selectionModel, oldFocused, newFocused) { - this.fireEvent('valuefocuschange', this, oldFocused, newFocused); - }, - onDestroy: function() { - this.selectionModel = Ext.destroy(this.selectionModel); - // This will unbind the store, which will destroy the valueStore - this.callParent(arguments); - }, - getSubTplData: function(fieldData) { - var me = this, - data = me.callParent(arguments), - emptyText = me.emptyText, - emptyInputCls = me.emptyInputCls, - isEmpty = emptyText && data.value.length < 1, - growMin = me.growMin, - growMax = me.growMax, - wrapperStyle = ''; - data.value = ''; - data.emptyText = isEmpty ? emptyText : ''; - data.emptyCls = isEmpty ? me.emptyCls : emptyInputCls; - data.inputElCls = isEmpty ? emptyInputCls : ''; - data.itemListCls = ''; - if (me.grow) { - if (Ext.isNumber(growMin) && growMin > 0) { - wrapperStyle += 'min-height:' + growMin + 'px;'; - } - if (Ext.isNumber(growMax) && growMax > 0) { - wrapperStyle += 'max-height:' + growMax + 'px;'; - } - } - data.wrapperStyle = wrapperStyle; - if (me.stacked === true) { - data.itemListCls += ' ' + Ext.baseCSSPrefix + 'tagfield-stacked'; - } - if (!me.multiSelect) { - data.itemListCls += ' ' + Ext.baseCSSPrefix + 'tagfield-singleselect'; - } - return data; - }, - afterRender: function() { - var me = this, - inputEl = me.inputEl, - emptyText = me.emptyText; - if (emptyText) { - // We remove HTML5 placeholder here because we use the emptyEl instead. - if (Ext.supports.Placeholder && inputEl) { - inputEl.dom.removeAttribute('placeholder'); - } else { - me.applyEmptyText(); - } - } - me.applyMultiselectItemMarkup(); - me.callParent(arguments); - }, - findRecord: function(field, value) { - var matches = this.getStore().queryRecords(field, value); - return matches.length ? matches[0] : false; - }, - /** - * Get the current cursor position in the input field, for key-based navigation - * @private - */ - getCursorPosition: function() { - var cursorPos; - if (document.selection) { - cursorPos = document.selection.createRange(); - cursorPos.collapse(true); - cursorPos.moveStart('character', -this.inputEl.dom.value.length); - cursorPos = cursorPos.text.length; - } else { - cursorPos = this.inputEl.dom.selectionStart; - } - return cursorPos; - }, - /** - * Check to see if the input field has selected text, for key-based navigation - * @private - */ - hasSelectedText: function() { - var inputEl = this.inputEl.dom, - sel, range; - if (document.selection) { - sel = document.selection; - range = sel.createRange(); - return (range.parentElement() === inputEl); - } else { - return inputEl.selectionStart !== inputEl.selectionEnd; - } - }, - /** - * Handles keyDown processing of key-based selection of labeled items. - * Supported keyboard controls: - * - * - If pick list is expanded - * - * - `CTRL-A` will select all the items in the pick list - * - * - If the cursor is at the beginning of the input field and there are values present - * - * - `CTRL-A` will highlight all the currently selected values - * - `BACKSPACE` and `DELETE` will remove any currently highlighted selected values - * - `RIGHT` and `LEFT` will move the current highlight in the appropriate direction - * - `SHIFT-RIGHT` and `SHIFT-LEFT` will add to the current highlight in the appropriate direction - * - * @protected - */ - onKeyDown: function(e) { - var me = this, - key = e.getKey(), - inputEl = me.inputEl, - rawValue = inputEl.dom.value, - valueCollection = me.valueCollection, - selModel = me.selectionModel, - stopEvent = false, - lastSelectionIndex; - if (me.readOnly || me.disabled || !me.editable) { - return; - } - if (valueCollection.getCount() > 0 && (rawValue === '' || (me.getCursorPosition() === 0 && !me.hasSelectedText()))) { - // Keyboard navigation of current values - lastSelectionIndex = (selModel.getCount() > 0) ? valueCollection.indexOf(selModel.getLastSelected()) : -1; - if (key === e.BACKSPACE || key === e.DELETE) { - // Delete token - if (lastSelectionIndex > -1) { - if (selModel.getCount() > 1) { - lastSelectionIndex = -1; - } - valueCollection.remove(selModel.getSelection()); - } else { - valueCollection.remove(valueCollection.last()); - } - selModel.clearSelections(); - if (lastSelectionIndex > 0) { - selModel.select(lastSelectionIndex - 1); - } else if (valueCollection.getCount()) { - selModel.select(valueCollection.last()); - } - stopEvent = true; - } else if (key === e.RIGHT || key === e.LEFT) { - // Navigate and select tokens - if (lastSelectionIndex === -1 && key === e.LEFT) { - selModel.select(valueCollection.last()); - stopEvent = true; - } else if (lastSelectionIndex > -1) { - if (key === e.RIGHT) { - if (lastSelectionIndex < (valueCollection.getCount() - 1)) { - selModel.select(lastSelectionIndex + 1, e.shiftKey); - stopEvent = true; - } else if (!e.shiftKey) { - selModel.deselectAll(); - stopEvent = true; - } - } else if (key === e.LEFT && (lastSelectionIndex > 0)) { - selModel.select(lastSelectionIndex - 1, e.shiftKey); - stopEvent = true; - } - } - } else if (key === e.A && e.ctrlKey) { - // Select all tokens - selModel.selectAll(); - stopEvent = e.A; - } - } - if (stopEvent) { - me.preventKeyUpEvent = stopEvent; - e.stopEvent(); - return; - } - // Prevent key up processing for enter if it is being handled by the picker - if (me.isExpanded && key === e.ENTER && me.picker.highlightedItem) { - me.preventKeyUpEvent = true; - } - if (me.enableKeyEvents) { - me.callParent(arguments); - } - if (!e.isSpecialKey() && !e.hasModifier()) { - selModel.deselectAll(); - } - }, - /** - * Handles auto-selection and creation of labeled items based on this field's - * delimiter, as well as the keyUp processing of key-based selection of labeled items. - * @protected - */ - onKeyUp: function(e, t) { - var me = this, - inputEl = me.inputEl, - rawValue = inputEl.dom.value, - preventKeyUpEvent = me.preventKeyUpEvent; - if (me.preventKeyUpEvent) { - e.stopEvent(); - if (preventKeyUpEvent === true || e.getKey() === preventKeyUpEvent) { - delete me.preventKeyUpEvent; - } - return; - } - if (me.multiSelect && me.delimiterRegexp && me.delimiterRegexp.test(rawValue) || (me.createNewOnEnter && e.getKey() === e.ENTER)) { - rawValue = Ext.Array.clean(rawValue.split(me.delimiterRegexp)); - inputEl.dom.value = ''; - me.setValue(me.valueStore.getRange().concat(rawValue)); - inputEl.focus(); - } - me.callParent([ - e, - t - ]); - }, - /** - * Overridden to get and set the DOM value directly for type-ahead suggestion (bypassing get/setRawValue) - * @protected - */ - onTypeAhead: function() { - var me = this, - displayField = me.displayField, - inputElDom = me.inputEl.dom, - boundList = me.getPicker(), - record = me.getStore().findRecord(displayField, inputElDom.value), - newValue, len, selStart; - if (record) { - newValue = record.get(displayField); - len = newValue.length; - selStart = inputElDom.value.length; - boundList.highlightItem(boundList.getNode(record)); - if (selStart !== 0 && selStart !== len) { - inputElDom.value = newValue; - me.selectText(selStart, newValue.length); - } - } - }, - /** - * Delegation control for selecting and removing labeled items or triggering list collapse/expansion - * @protected - */ - onItemListClick: function(e) { - var me = this, - selectionModel = me.selectionModel, - itemEl = e.getTarget(me.tagItemSelector), - closeEl = itemEl ? e.getTarget(me.tagItemCloseSelector) : false; - if (me.readOnly || me.disabled) { - return; - } - e.stopPropagation(); - if (itemEl) { - if (closeEl) { - me.removeByListItemNode(itemEl); - if (me.valueStore.getCount() > 0) { - me.fireEvent('select', me, me.valueStore.getRange()); - } - } else { - me.toggleSelectionByListItemNode(itemEl, e.shiftKey); - } - // If not using touch interactions, focus the input - if (!Ext.supports.TouchEvents) { - me.inputEl.focus(); - } - } else { - if (selectionModel.getCount() > 0) { - selectionModel.deselectAll(); - } - me.inputEl.focus(); - if (me.triggerOnClick) { - me.onTriggerClick(); - } - } - }, - // Prevent item from receiving focus. - // See EXTJS-17686. - onItemMouseDown: function(e) { - e.preventDefault(); - }, - /** - * Build the markup for the labeled items. Template must be built on demand due to ComboBox initComponent - * life cycle for the creation of on-demand stores (to account for automatic valueField/displayField setting) - * @private - */ - getMultiSelectItemMarkup: function() { - var me = this, - childElCls = (me._getChildElCls && me._getChildElCls()) || ''; - // hook for rtl cls - if (!me.multiSelectItemTpl) { - if (!me.labelTpl) { - me.labelTpl = '{' + me.displayField + '}'; - } - me.labelTpl = me.getTpl('labelTpl'); - if (me.tipTpl) { - me.tipTpl = me.getTpl('tipTpl'); - } - me.multiSelectItemTpl = new Ext.XTemplate([ - '', - '
  • ', - ' ' + me.tagSelectedCls, - '', - '{%', - 'values = values.data;', - '%}', - me.tipTpl ? '" data-qtip="{[this.getTip(values)]}">' : '">', - '
    {[this.getItemLabel(values)]}
    ', - '
    ', - '
  • ', - '
    ', - { - isSelected: function(rec) { - return me.selectionModel.isSelected(rec); - }, - getItemLabel: function(values) { - return Ext.String.htmlEncode(me.labelTpl.apply(values)); - }, - getTip: function(values) { - return Ext.String.htmlEncode(me.tipTpl.apply(values)); - }, - strict: true - } - ]); - } - if (!me.multiSelectItemTpl.isTemplate) { - me.multiSelectItemTpl = this.getTpl('multiSelectItemTpl'); - } - return me.multiSelectItemTpl.apply(me.valueCollection.getRange()); - }, - /** - * Update the labeled items rendering - * @private - */ - applyMultiselectItemMarkup: function() { - var me = this, - itemList = me.itemList; - if (itemList) { - itemList.select('.' + Ext.baseCSSPrefix + 'tagfield-item').destroy(); - me.inputElCt.insertHtml('beforeBegin', me.getMultiSelectItemMarkup()); - me.autoSize(); - } - }, - /** - * Returns the record from valueStore for the labeled item node - */ - getRecordByListItemNode: function(itemEl) { - return this.valueCollection.items[Number(itemEl.getAttribute('data-selectionIndex'))]; - }, - /** - * Toggle of labeled item selection by node reference - */ - toggleSelectionByListItemNode: function(itemEl, keepExisting) { - var me = this, - rec = me.getRecordByListItemNode(itemEl), - selModel = me.selectionModel; - if (rec) { - if (selModel.isSelected(rec)) { - selModel.deselect(rec); - } else { - selModel.select(rec, keepExisting); - } - } - }, - /** - * Removal of labelled item by node reference - */ - removeByListItemNode: function(itemEl) { - var me = this, - rec = me.getRecordByListItemNode(itemEl); - if (rec) { - me.pickerSelectionModel.deselect(rec); - } - }, - // Private implementation. - // The display value is always the raw value. - // Picked values are displayed by the tag template. - getDisplayValue: function() { - return this.getRawValue(); - }, - /** - * @inheritdoc - * Intercept calls to getRawValue to pretend there is no inputEl for rawValue handling, - * so that we can use inputEl for user input of just the current value. - */ - getRawValue: function() { - var me = this, - records = me.getValueRecords(), - values = [], - i, len; - for (i = 0 , len = records.length; i < len; i++) { - values.push(records[i].data[me.displayField]); - } - return values.join(','); - }, - setRawValue: function(value) { - // setRawValue is not supported for tagfield. - return; - }, - /** - * Removes a value or values from the current value of the field - * @param {Mixed} value The value or values to remove from the current value, see {@link #setValue} - */ - removeValue: function(value) { - var me = this, - valueCollection = me.valueCollection, - len, i, item, - toRemove = []; - if (value) { - value = Ext.Array.from(value); - // Ensure that the remove values are records - for (i = 0 , len = value.length; i < len; ++i) { - item = value[i]; - // If a key is supplied, find the matching value record from our value collection - if (!item.isModel) { - item = valueCollection.byValue.get(item); - } - if (item) { - toRemove.push(item); - } - } - me.valueCollection.beginUpdate(); - me.pickerSelectionModel.deselect(toRemove); - me.valueCollection.endUpdate(); - } - }, - /** - * Sets the specified value(s) into the field. The following value formats are recognized: - * - * - Single Values - * - * - A string associated to this field's configured {@link #valueField} - * - A record containing at least this field's configured {@link #valueField} and {@link #displayField} - * - * - Multiple Values - * - * - If {@link #multiSelect} is `true`, a string containing multiple strings as - * specified in the Single Values section above, concatenated in to one string - * with each entry separated by this field's configured {@link #delimiter} - * - An array of strings as specified in the Single Values section above - * - An array of records as specified in the Single Values section above - * - * In any of the string formats above, the following occurs if an associated record cannot be found: - * - * 1. If {@link #forceSelection} is `false`, a new record of the {@link #store}'s configured model type - * will be created using the given value as the {@link #displayField} and {@link #valueField}. - * This record will be added to the current value, but it will **not** be added to the store. - * 2. If {@link #forceSelection} is `true` and {@link #queryMode} is `remote`, the list of unknown - * values will be submitted as a call to the {@link #store}'s load as a parameter named by - * the {@link #valueParam} with values separated by the configured {@link #delimiter}. - * ** This process will cause setValue to asynchronously process. ** This will only be attempted - * once. Any unknown values that the server does not return records for will be removed. - * 3. Otherwise, unknown values will be removed. - * - * @param {Mixed} value The value(s) to be set, see method documentation for details - * @return {Ext.form.field.Field/Boolean} this, or `false` if asynchronously querying for unknown values - */ - setValue: function(value, /* private */ - add, skipLoad) { - var me = this, - valueStore = me.valueStore, - valueField = me.valueField, - unknownValues = [], - store = me.store, - autoLoadOnValue = me.autoLoadOnValue, - isLoaded = store.getCount() > 0 || store.isLoaded(), - pendingLoad = store.hasPendingLoad(), - unloaded = autoLoadOnValue && !isLoaded && !pendingLoad, - record, len, i, valueRecord, cls, params; - if (Ext.isEmpty(value)) { - value = null; - } else if (Ext.isString(value) && me.multiSelect) { - value = value.split(me.delimiter); - } else { - value = Ext.Array.from(value, true); - } - if (value && me.queryMode === 'remote' && !store.isEmptyStore && skipLoad !== true && unloaded) { - for (i = 0 , len = value.length; i < len; i++) { - record = value[i]; - if (!record || !record.isModel) { - valueRecord = valueStore.findExact(valueField, record); - if (valueRecord > -1) { - value[i] = valueStore.getAt(valueRecord); - } else { - valueRecord = me.findRecord(valueField, record); - if (!valueRecord) { - if (me.forceSelection) { - unknownValues.push(record); - } else { - valueRecord = {}; - valueRecord[me.valueField] = record; - valueRecord[me.displayField] = record; - cls = me.valueStore.getModel(); - valueRecord = new cls(valueRecord); - } - } - if (valueRecord) { - value[i] = valueRecord; - } - } - } - } - if (unknownValues.length) { - params = {}; - params[me.valueParam || me.valueField] = unknownValues.join(me.delimiter); - store.load({ - params: params, - callback: function() { - me.setValue(value, add, true); - me.autoSize(); - me.lastQuery = false; - } - }); - return false; - } - } - // For single-select boxes, use the last good (formal record) value if possible - if (!me.multiSelect && value.length > 0) { - for (i = value.length - 1; i >= 0; i--) { - if (value[i].isModel) { - value = value[i]; - break; - } - } - if (Ext.isArray(value)) { - value = value[value.length - 1]; - } - } - return me.callParent([ - value, - add - ]); - }, - // Private internal setting of value when records are added to the valueCollection - // setValue itself adds to the valueCollection. - updateValue: function() { - var me = this, - valueArray = me.valueCollection.getRange(), - len = valueArray.length, - i; - for (i = 0; i < len; i++) { - valueArray[i] = valueArray[i].get(me.valueField); - } - // Set the value of this field. If we are multi-selecting, then that is an array. - me.setHiddenValue(valueArray); - me.value = me.multiSelect ? valueArray : valueArray[0]; - if (!Ext.isDefined(me.value)) { - me.value = undefined; - } - me.applyMultiselectItemMarkup(); - me.checkChange(); - me.applyEmptyText(); - }, - /** - * Returns the records for the field's current value - * @return {Array} The records for the field's current value - */ - getValueRecords: function() { - return this.valueCollection.getRange(); - }, - /** - * @inheritdoc - * Overridden to optionally allow for submitting the field as a json encoded array. - */ - getSubmitData: function() { - var me = this, - val = me.callParent(arguments); - if (me.multiSelect && me.encodeSubmitValue && val && val[me.name]) { - val[me.name] = Ext.encode(val[me.name]); - } - return val; - }, - /** - * Overridden to handle partial-input selections more directly - */ - assertValue: function() { - var me = this, - rawValue = me.inputEl.dom.value, - rec = !Ext.isEmpty(rawValue) ? me.findRecordByDisplay(rawValue) : false, - value = false; - if (!rec && !me.forceSelection && me.createNewOnBlur && !Ext.isEmpty(rawValue)) { - value = rawValue; - } else if (rec) { - value = rec; - } - if (value) { - me.addValue(value); - } - me.inputEl.dom.value = ''; - me.collapse(); - }, - /** - * Overridden to be more accepting of varied value types - */ - isEqual: function(v1, v2) { - var fromArray = Ext.Array.from, - valueField = this.valueField, - i, len, t1, t2; - v1 = fromArray(v1); - v2 = fromArray(v2); - len = v1.length; - if (len !== v2.length) { - return false; - } - for (i = 0; i < len; i++) { - t1 = v1[i].isModel ? v1[i].get(valueField) : v1[i]; - t2 = v2[i].isModel ? v2[i].get(valueField) : v2[i]; - if (t1 !== t2) { - return false; - } - } - return true; - }, - /** - * Overridden to use value (selection) instead of raw value and to avoid the use of placeholder - */ - applyEmptyText: function() { - var me = this, - emptyText = me.emptyText, - emptyEl = me.emptyEl, - inputEl = me.inputEl, - listWrapper = me.listWrapper, - emptyCls = me.emptyCls, - emptyInputCls = me.emptyInputCls, - isEmpty; - if (me.rendered && emptyText) { - isEmpty = Ext.isEmpty(me.value) && !me.hasFocus; - if (isEmpty) { - inputEl.dom.value = ''; - emptyEl.setHtml(emptyText); - emptyEl.addCls(emptyCls); - emptyEl.removeCls(emptyInputCls); - listWrapper.addCls(emptyCls); - inputEl.addCls(emptyInputCls); - } else { - emptyEl.addCls(emptyInputCls); - emptyEl.removeCls(emptyCls); - listWrapper.removeCls(emptyCls); - inputEl.removeCls(emptyInputCls); - } - me.autoSize(); - } - }, - /** - * Overridden to use inputEl instead of raw value and to avoid the use of placeholder - */ - preFocus: function() { - var me = this, - inputEl = me.inputEl, - isEmpty = inputEl.dom.value === ''; - me.emptyEl.addCls(me.emptyInputCls); - me.emptyEl.removeCls(me.emptyCls); - me.listWrapper.removeCls(me.emptyCls); - me.inputEl.removeCls(me.emptyInputCls); - if (me.selectOnFocus || isEmpty) { - inputEl.dom.select(); - } - }, - /** - * Intercept calls to onFocus to add focusCls, because the base field - * classes assume this should be applied to inputEl - */ - onFocus: function() { - var me = this, - focusCls = me.focusCls, - itemList = me.itemList; - if (focusCls && itemList) { - itemList.addCls(focusCls); - } - me.callParent(arguments); - }, - /** - * Intercept calls to onBlur to remove focusCls, because the base field - * classes assume this should be applied to inputEl - */ - onBlur: function() { - var me = this, - focusCls = me.focusCls, - itemList = me.itemList; - if (focusCls && itemList) { - itemList.removeCls(focusCls); - } - me.callParent(arguments); - }, - /** - * Intercept calls to renderActiveError to add invalidCls, because the base - * field classes assume this should be applied to inputEl - */ - renderActiveError: function() { - var me = this, - invalidCls = me.invalidCls, - itemList = me.itemList, - hasError = me.hasActiveError(); - if (invalidCls && itemList) { - itemList[hasError ? 'addCls' : 'removeCls'](me.invalidCls + '-field'); - } - me.callParent(arguments); - }, - /** - * Initiate auto-sizing for height based on {@link #grow}, if applicable. - */ - autoSize: function() { - var me = this; - if (me.grow && me.rendered) { - me.autoSizing = true; - me.updateLayout(); - } - return me; - }, - /** - * Track height change to fire {@link #event-autosize} event, when applicable. - */ - afterComponentLayout: function() { - var me = this, - height; - if (me.autoSizing) { - height = me.getHeight(); - if (height !== me.lastInputHeight) { - if (me.isExpanded) { - me.alignPicker(); - } - me.fireEvent('autosize', me, height); - me.lastInputHeight = height; - me.autoSizing = false; - } - } - } -}); - -/** - * A time picker which provides a list of times from which to choose. This is used by the Ext.form.field.Time - * class to allow browsing and selection of valid times, but could also be used with other components. - * - * By default, all times starting at midnight and incrementing every 15 minutes will be presented. This list of - * available times can be controlled using the {@link #minValue}, {@link #maxValue}, and {@link #increment} - * configuration properties. The format of the times presented in the list can be customized with the {@link #format} - * config. - * - * To handle when the user selects a time from the list, you can subscribe to the {@link #selectionchange} event. - * - * @example - * Ext.create('Ext.picker.Time', { - * width: 60, - * minValue: Ext.Date.parse('04:30:00 AM', 'h:i:s A'), - * maxValue: Ext.Date.parse('08:00:00 AM', 'h:i:s A'), - * renderTo: Ext.getBody() - * }); - */ -Ext.define('Ext.picker.Time', { - extend: 'Ext.view.BoundList', - alias: 'widget.timepicker', - requires: [ - 'Ext.data.Store', - 'Ext.Date' - ], - config: { - /** - * @hide - * This class creates its own store based upon time range and increment configuration. - */ - store: true - }, - statics: { - /** - * @private - * Creates the internal {@link Ext.data.Store} that contains the available times. The store - * is loaded with all possible times, and it is later filtered to hide those times outside - * the minValue/maxValue. - */ - createStore: function(format, increment) { - var dateUtil = Ext.Date, - clearTime = dateUtil.clearTime, - initDate = this.prototype.initDate, - times = [], - min = clearTime(new Date(initDate[0], initDate[1], initDate[2])), - max = dateUtil.add(clearTime(new Date(initDate[0], initDate[1], initDate[2])), 'mi', (24 * 60) - 1); - while (min <= max) { - times.push({ - disp: dateUtil.dateFormat(min, format), - date: min - }); - min = dateUtil.add(min, 'mi', increment); - } - return new Ext.data.Store({ - model: Ext.picker.Time.prototype.modelType, - data: times - }); - } - }, - /** - * @cfg {Date} minValue - * The minimum time to be shown in the list of times. This must be a Date object (only the time fields will be - * used); no parsing of String values will be done. - */ - /** - * @cfg {Date} maxValue - * The maximum time to be shown in the list of times. This must be a Date object (only the time fields will be - * used); no parsing of String values will be done. - */ - /** - * @cfg {Number} increment - * The number of minutes between each time value in the list. - */ - increment: 15, - // - /** - * @cfg {String} [format=undefined] - * The default time format string which can be overriden for localization support. The format must be valid - * according to {@link Ext.Date#parse}. - * - * Defaults to `'g:i A'`, e.g., `'3:15 PM'`. For 24-hour time format try `'H:i'` instead. - */ - format: "g:i A", - // - /** - * @private - * The field in the implicitly-generated Model objects that gets displayed in the list. This is - * an internal field name only and is not useful to change via config. - */ - displayField: 'disp', - /** - * @private - * Year, month, and day that all times will be normalized into internally. - */ - initDate: [ - 2008, - 0, - 1 - ], - componentCls: Ext.baseCSSPrefix + 'timepicker', - /** - * @cfg - * @private - */ - loadMask: false, - initComponent: function() { - var me = this, - dateUtil = Ext.Date, - clearTime = dateUtil.clearTime, - initDate = me.initDate; - // Set up absolute min and max for the entire day - me.absMin = clearTime(new Date(initDate[0], initDate[1], initDate[2])); - me.absMax = dateUtil.add(clearTime(new Date(initDate[0], initDate[1], initDate[2])), 'mi', (24 * 60) - 1); - // Updates the range filter's filterFn according to our configured min and max - me.updateList(); - me.callParent(); - }, - setStore: function(store) { - // TimePicker may be used standalone without being configured as a BoundList by a Time field. - // In this case, we have to create our own store. - this.store = (store === true) ? Ext.picker.Time.createStore(this.format, this.increment) : store; - }, - /** - * Set the {@link #minValue} and update the list of available times. This must be a Date object (only the time - * fields will be used); no parsing of String values will be done. - * @param {Date} value - */ - setMinValue: function(value) { - this.minValue = value; - this.updateList(); - }, - /** - * Set the {@link #maxValue} and update the list of available times. This must be a Date object (only the time - * fields will be used); no parsing of String values will be done. - * @param {Date} value - */ - setMaxValue: function(value) { - this.maxValue = value; - this.updateList(); - }, - /** - * @private - * Sets the year/month/day of the given Date object to the {@link #initDate}, so that only - * the time fields are significant. This makes values suitable for time comparison. - * @param {Date} date - */ - normalizeDate: function(date) { - var initDate = this.initDate; - date.setFullYear(initDate[0], initDate[1], initDate[2]); - return date; - }, - /** - * Update the list of available times in the list to be constrained within the {@link #minValue} - * and {@link #maxValue}. - */ - updateList: function() { - var me = this, - min = me.normalizeDate(me.minValue || me.absMin), - max = me.normalizeDate(me.maxValue || me.absMax), - filters = me.getStore().getFilters(), - filter = me.rangeFilter; - filters.beginUpdate(); - if (filter) { - filters.remove(filter); - } - filter = me.rangeFilter = new Ext.util.Filter({ - filterFn: function(record) { - var date = record.get('date'); - return date >= min && date <= max; - } - }); - filters.add(filter); - filters.endUpdate(); - } -}, function() { - this.prototype.modelType = Ext.define(null, { - extend: 'Ext.data.Model', - fields: [ - 'disp', - 'date' - ] - }); -}); - -/** - * Provides a time input field with a time dropdown and automatic time validation. - * - * This field recognizes and uses JavaScript Date objects as its main {@link #value} type (only the time portion of the - * date is used; the month/day/year are ignored). In addition, it recognizes string values which are parsed according to - * the {@link #format} and/or {@link #altFormats} configs. These may be reconfigured to use time formats appropriate for - * the user's locale. - * - * The field may be limited to a certain range of times by using the {@link #minValue} and {@link #maxValue} configs, - * and the interval between time options in the dropdown can be changed with the {@link #increment} config. - * - * Example usage: - * - * @example - * Ext.create('Ext.form.Panel', { - * title: 'Time Card', - * width: 300, - * bodyPadding: 10, - * renderTo: Ext.getBody(), - * items: [{ - * xtype: 'timefield', - * name: 'in', - * fieldLabel: 'Time In', - * minValue: '6:00 AM', - * maxValue: '8:00 PM', - * increment: 30, - * anchor: '100%' - * }, { - * xtype: 'timefield', - * name: 'out', - * fieldLabel: 'Time Out', - * minValue: '6:00 AM', - * maxValue: '8:00 PM', - * increment: 30, - * anchor: '100%' - * }] - * }); - */ -Ext.define('Ext.form.field.Time', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.timefield', - requires: [ - 'Ext.form.field.Date', - 'Ext.picker.Time', - 'Ext.view.BoundListKeyNav', - 'Ext.Date' - ], - alternateClassName: [ - 'Ext.form.TimeField', - 'Ext.form.Time' - ], - /** - * @cfg {String} [triggerCls='x-form-time-trigger'] - * An additional CSS class used to style the trigger button. The trigger will always get the {@link Ext.form.trigger.Trigger#baseCls} - * by default and triggerCls will be **appended** if specified. - */ - triggerCls: Ext.baseCSSPrefix + 'form-time-trigger', - /** - * @cfg {Date/String} minValue - * The minimum allowed time. Can be either a Javascript date object with a valid time value or a string time in a - * valid format -- see {@link #format} and {@link #altFormats}. - */ - /** - * @cfg {Date/String} maxValue - * The maximum allowed time. Can be either a Javascript date object with a valid time value or a string time in a - * valid format -- see {@link #format} and {@link #altFormats}. - */ - // - /** - * @cfg {String} minText - * The error text to display when the entered time is before {@link #minValue}. - */ - minText: "The time in this field must be equal to or after {0}", - // - // - /** - * @cfg {String} maxText - * The error text to display when the entered time is after {@link #maxValue}. - */ - maxText: "The time in this field must be equal to or before {0}", - // - // - /** - * @cfg {String} invalidText - * The error text to display when the time in the field is invalid. - */ - invalidText: "{0} is not a valid time", - // - // - /** - * @cfg {String} [format=undefined] - * The default time format string which can be overridden for localization support. - * The format must be valid according to {@link Ext.Date#parse}. - * - * Defaults to `'g:i A'`, e.g., `'3:15 PM'`. For 24-hour time format try `'H:i'` instead. - */ - format: "g:i A", - // - // - /** - * @cfg {String} [submitFormat=undefined] - * The date format string which will be submitted to the server. The format must be valid according to - * {@link Ext.Date#parse}. - * - * Defaults to {@link #format}. - */ - // - // - /** - * @cfg {String} altFormats - * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined - * format. - */ - altFormats: "g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H|gi a|hi a|giA|hiA|gi A|hi A", - // - // - // The default format for the time field is 'g:i A', which is hardly informative - /** - * @cfg {String} formatText - * The format text to be announced by screen readers when the field is focused. - */ - /** @ignore */ - formatText: 'Expected time format: HH:MM space am/pm', - // - /** - * @cfg {Number} [increment=15] - * The number of minutes between each time value in the list. - * - * Note that this only affects the *list of suggested times.* - * - * To enforce that only times on the list are valid, use {@link #snapToIncrement}. That will coerce - * any typed values to the nearest increment point upon blur. - */ - increment: 15, - /** - * @cfg {Number} pickerMaxHeight - * The maximum height of the {@link Ext.picker.Time} dropdown. - */ - pickerMaxHeight: 300, - /** - * @cfg {Boolean} selectOnTab - * Whether the Tab key should select the currently highlighted item. - */ - selectOnTab: true, - /** - * @cfg {Boolean} [snapToIncrement=false] - * Specify as `true` to enforce that only values on the {@link #increment} boundary are accepted. - * - * Typed values will be coerced to the nearest {@link #increment} point on blur. - */ - snapToIncrement: false, - /** - * @cfg - * @inheritdoc - */ - valuePublishEvent: [ - 'select', - 'blur' - ], - /** - * @private - * This is the date to use when generating time values in the absence of either minValue - * or maxValue. Using the current date causes DST issues on DST boundary dates, so this is an - * arbitrary "safe" date that can be any date aside from DST boundary dates. - */ - initDate: '1/1/2008', - initDateParts: [ - 2008, - 0, - 1 - ], - initDateFormat: 'j/n/Y', - queryMode: 'local', - displayField: 'disp', - valueField: 'date', - initComponent: function() { - var me = this, - min = me.minValue, - max = me.maxValue; - if (min) { - me.setMinValue(min); - } - if (max) { - me.setMaxValue(max); - } - me.displayTpl = new Ext.XTemplate('' + '{[typeof values === "string" ? values : this.formatDate(values["' + me.displayField + '"])]}' + '' + me.delimiter + '' + '', { - formatDate: me.formatDate.bind(me) - }); - // Create a store of times. - me.store = Ext.picker.Time.createStore(me.format, me.increment); - me.callParent(); - // Ensure time constraints are applied to the store. - // TimePicker does this on create. - me.getPicker(); - }, - /** - * @private - */ - isEqual: function(v1, v2) { - var fromArray = Ext.Array.from, - isEqual = Ext.Date.isEqual, - i, len; - v1 = fromArray(v1); - v2 = fromArray(v2); - len = v1.length; - if (len !== v2.length) { - return false; - } - for (i = 0; i < len; i++) { - if (!(v2[i] instanceof Date) || !(v1[i] instanceof Date) || !isEqual(v2[i], v1[i])) { - return false; - } - } - return true; - }, - /** - * Replaces any existing {@link #minValue} with the new time and refreshes the picker's range. - * @param {Date/String} value The minimum time that can be selected - */ - setMinValue: function(value) { - var me = this, - picker = me.picker; - me.setLimit(value, true); - if (picker) { - picker.setMinValue(me.minValue); - } - }, - /** - * Replaces any existing {@link #maxValue} with the new time and refreshes the picker's range. - * @param {Date/String} value The maximum time that can be selected - */ - setMaxValue: function(value) { - var me = this, - picker = me.picker; - me.setLimit(value, false); - if (picker) { - picker.setMaxValue(me.maxValue); - } - }, - /** - * @private - * Updates either the min or max value. Converts the user's value into a Date object whose - * year/month/day is set to the {@link #initDate} so that only the time fields are significant. - */ - setLimit: function(value, isMin) { - var me = this, - d, val; - if (Ext.isString(value)) { - d = me.parseDate(value); - } else if (Ext.isDate(value)) { - d = value; - } - if (d) { - val = me.getInitDate(); - val.setHours(d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds()); - } else // Invalid min/maxValue config should result in a null so that defaulting takes over - { - val = null; - } - me[isMin ? 'minValue' : 'maxValue'] = val; - }, - getInitDate: function(hours, minutes, seconds) { - var parts = this.initDateParts; - return new Date(parts[0], parts[1], parts[2], hours || 0, minutes || 0, seconds || 0, 0); - }, - valueToRaw: function(value) { - return this.formatDate(this.parseDate(value)); - }, - /** - * Runs all of Time's validations and returns an array of any errors. Note that this first runs Text's validations, - * so the returned array is an amalgamation of all field errors. The additional validation checks are testing that - * the time format is valid, that the chosen time is within the {@link #minValue} and {@link #maxValue} constraints - * set. - * @param {Object} [value] The value to get errors for (defaults to the current field value) - * @return {String[]} All validation errors for this field - */ - getErrors: function(value) { - value = arguments.length > 0 ? value : this.getRawValue(); - var me = this, - format = Ext.String.format, - errors = me.callParent([ - value - ]), - minValue = me.minValue, - maxValue = me.maxValue, - data = me.displayTplData, - raw = me.getRawValue(), - i, len, date, item; - if (data && data.length > 0) { - for (i = 0 , len = data.length; i < len; i++) { - item = data[i]; - item = item.date || item.disp; - date = me.parseDate(item); - if (!date) { - errors.push(format(me.invalidText, item, Ext.Date.unescapeFormat(me.format))); - - continue; - } - if (minValue && date < minValue) { - errors.push(format(me.minText, me.formatDate(minValue))); - } - if (maxValue && date > maxValue) { - errors.push(format(me.maxText, me.formatDate(maxValue))); - } - } - } else if (raw.length && !me.parseDate(raw)) { - // If we don't have any data & a rawValue, it means an invalid time was entered. - errors.push(format(me.invalidText, raw, Ext.Date.unescapeFormat(me.format))); - } - return errors; - }, - formatDate: function(items) { - var formatted = [], - i, len; - items = Ext.Array.from(items); - for (i = 0 , len = items.length; i < len; i++) { - formatted.push(Ext.form.field.Date.prototype.formatDate.call(this, items[i])); - } - return formatted.join(this.delimiter); - }, - /** - * @private - * Parses an input value into a valid Date object. - * @param {String/Date} value - */ - parseDate: function(value) { - var me = this, - val = value, - altFormats = me.altFormats, - altFormatsArray = me.altFormatsArray, - i = 0, - len; - if (value && !Ext.isDate(value)) { - val = me.safeParse(value, me.format); - if (!val && altFormats) { - altFormatsArray = altFormatsArray || altFormats.split('|'); - len = altFormatsArray.length; - for (; i < len && !val; ++i) { - val = me.safeParse(value, altFormatsArray[i]); - } - } - } - // If configured to snap, snap resulting parsed Date to the closest increment. - if (val && me.snapToIncrement) { - val = new Date(Ext.Number.snap(val.getTime(), me.increment * 60 * 1000)); - } - return val; - }, - safeParse: function(value, format) { - var me = this, - utilDate = Ext.Date, - parsedDate, - result = null; - if (utilDate.formatContainsDateInfo(format)) { - // assume we've been given a full date - result = utilDate.parse(value, format); - } else { - // Use our initial safe date - parsedDate = utilDate.parse(me.initDate + ' ' + value, me.initDateFormat + ' ' + format); - if (parsedDate) { - result = parsedDate; - } - } - return result; - }, - /** - * @private - */ - getSubmitValue: function() { - var me = this, - format = me.submitFormat || me.format, - value = me.getValue(); - return value ? Ext.Date.format(value, format) : null; - }, - /** - * @private - * Creates the {@link Ext.picker.Time} - */ - createPicker: function() { - var me = this; - me.listConfig = Ext.apply({ - xtype: 'timepicker', - pickerField: me, - cls: undefined, - minValue: me.minValue, - maxValue: me.maxValue, - increment: me.increment, - format: me.format, - maxHeight: me.pickerMaxHeight - }, me.listConfig); - return me.callParent(); - }, - completeEdit: function() { - var me = this, - val = me.getValue(); - me.callParent(arguments); - // Only set the raw value if the current value is valid and is not falsy - if (me.validateValue(val)) { - me.setValue(val); - } - }, - /** - * Finds the record by searching values in the {@link #valueField}. - * @param {Object/String} value The value to match the field against. - * @return {Ext.data.Model} The matched record or false. - */ - findRecordByValue: function(value) { - if (typeof value === 'string') { - value = this.parseDate(value); - } - return this.callParent([ - value - ]); - }, - rawToValue: function(item) { - var me = this, - items, values, i, len; - if (me.multiSelect) { - values = []; - items = Ext.Array.from(item); - for (i = 0 , len = items.length; i < len; i++) { - values.push(me.parseDate(items[i])); - } - return values; - } - return me.parseDate(item); - }, - setValue: function(v) { - var me = this; - // The timefield can get in a loop when creating its picker. For instance, when creating the picker, the - // timepicker will add a filter (see TimePicker#updateList) which will then trigger the checkValueOnChange - // listener which in turn calls into here, rinse and repeat. - if (me.creatingPicker) { - return; - } - // Store MUST be created for parent setValue to function. - me.getPicker(); - if (Ext.isDate(v)) { - v = me.getInitDate(v.getHours(), v.getMinutes(), v.getSeconds()); - } - return me.callParent([ - v - ]); - }, - getValue: function() { - return this.rawToValue(this.callParent(arguments)); - } -}); - -/** - * @deprecated 5.0 - * Provides a convenient wrapper for TextFields that adds a clickable trigger button. - * (looks like a combobox by default). - * - * As of Ext JS 5.0 this class has been deprecated. It is recommended to use a - * {@link Ext.form.field.Text Text Field} with the {@link Ext.form.field.Text#triggers - * triggers} config instead. This class is provided for compatibility reasons but is - * not used internally by the framework. - */ -Ext.define('Ext.form.field.Trigger', { - extend: 'Ext.form.field.Text', - alias: [ - 'widget.triggerfield', - 'widget.trigger' - ], - requires: [ - 'Ext.dom.Helper', - 'Ext.util.ClickRepeater' - ], - alternateClassName: [ - 'Ext.form.TriggerField', - 'Ext.form.TwinTriggerField', - 'Ext.form.Trigger' - ], - /** - * @cfg {String} triggerCls - * An additional CSS class used to style the trigger button. The trigger will always get the {@link Ext.form.trigger.Trigger#baseCls} - * by default and triggerCls will be **appended** if specified. - */ - triggerCls: Ext.baseCSSPrefix + 'form-arrow-trigger', - inheritableStatics: { - /** - * @private - * @static - * @inheritable - */ - warnDeprecated: function() { - // TODO: can we make this warning depend on compat level? - Ext.log.warn('Ext.form.field.Trigger is deprecated. Use Ext.form.field.Text instead.'); - } - }, - onClassExtended: function() { - this.warnDeprecated(); - }, - constructor: function(config) { - this.self.warnDeprecated(); - this.callParent([ - config - ]); - } -}); - -/** - * Instances of this class encapsulate a position in a grid's row/column coordinate system. - * - * Cells are addressed using the owning {@link #record} and {@link #column} for robustness. - * the column may be moved, the store may be sorted, and the CellContext will still reference - * the same *logical* cell. Be aware that due to buffered rendering the *physical* cell may not exist. - * - * The {@link #setPosition} method however allows a numeric row and column to be passed in. These - * are immediately converted. - * - * Be careful not to make `CellContext` objects *too* persistent. If the owning record is removed, or the owning column - * is removed, the reference will be stale. - * - * Freshly created context objects, such as those exposed by events from the {Ext.grid.selection.SpreadsheetModel spreadsheet selection model} - * are safe to use until your application mutates the store, or changes the column set. - */ -Ext.define('Ext.grid.CellContext', { - /** - * @property {Boolean} isCellContext - * @readonly - * `true` in this class to identify an object as an instantiated CellContext, or subclass thereof. - */ - isCellContext: true, - /** - * @readonly - * @property {Ext.grid.column.Column} column - * The grid column which owns the referenced cell. - */ - /** - * @readonly - * @property {Ext.data.Model} record - * The store record which maps to the referenced cell. - */ - /** - * @readonly - * @property {Number} rowIdx - * The row number in the store which owns the referenced cell. - * - * *Be aware that after the initial call to {@link #setPosition}, this value may become stale due to subsequent store mutation.* - */ - /** - * @readonly - * @property {Number} colIdx - * The column index in the owning View's leaf column set of the referenced cell. - * - * *Be aware that after the initial call to {@link #setPosition}, this value may become stale due to subsequent column mutation.* - */ - /** - * Creates a new CellContext which references a {@link Ext.view.Table GridView} - * @param {Ext.view.Table} view The {@link Ext.view.Table GridView} for which the cell context is needed. - * - * To complete creation of a valid context, use the {@link #setPosition} method. - */ - constructor: function(view) { - this.view = view; - }, - /** - * Binds this cell context to a logical cell defined by the {@link #record} and {@link #column}. - * - * @param {Number/Ext.data.Model} row The row index or record which owns the required cell. - * @param {Number/Ext.grid.column.Column} col The column index (In the owning View's leaf column set), or the owning {@link Ext.grid.column.Column column}. - * - * A one argument form may be used in the form of an array: - * - * [column, row] - * - * Or another CellContext may be passed. - * - * @return {Ext.grid.CellContext} this CellContext object. - */ - setPosition: function(row, col) { - var me = this; - // We were passed {row: 1, column: 2, view: myView} or [2, 1] - if (arguments.length === 1) { - // A [column, row] array passed - if (row.length) { - col = row[0]; - row = row[1]; - } else if (row.isCellContext) { - return me.setAll(row.view, row.rowIdx, row.colIdx, row.record, row.columnHeader); - } else // An object containing {row: r, column: c} - { - if (row.view) { - me.view = row.view; - } - col = row.column; - row = row.row; - } - } - me.setRow(row); - me.setColumn(col); - return me; - }, - setAll: function(view, recordIndex, columnIndex, record, columnHeader) { - var me = this; - me.view = view; - me.rowIdx = recordIndex; - me.colIdx = columnIndex; - me.record = record; - me.column = columnHeader; - return me; - }, - setRow: function(row) { - var me = this, - dataSource = me.view.dataSource; - if (row !== undefined) { - // Row index passed - if (typeof row === 'number') { - me.rowIdx = Math.max(Math.min(row, dataSource.getCount() - 1), 0); - me.record = dataSource.getAt(row); - } - // row is a Record - else if (row.isModel) { - me.record = row; - me.rowIdx = dataSource.indexOf(row); - } - // row is a grid row, or Element wrapping row - else if (row.tagName || row.isElement) { - me.record = me.view.getRecord(row); - me.rowIdx = dataSource.indexOf(me.record); - } - } - return me; - }, - setColumn: function(col) { - var me = this, - colMgr = me.view.getVisibleColumnManager(); - // Maintainer: - // We MUST NOT update the context view with the column's view because this context - // may be for an Ext.locking.View which spans two grid views, and a column references - // its local grid view. - if (col !== undefined) { - if (typeof col === 'number') { - me.colIdx = col; - me.column = colMgr.getHeaderAtIndex(col); - } else if (col.isHeader) { - me.column = col; - // Must use the Manager's indexOf because view may be a locking view - // And Column#getVisibleIndex returns the index of the column within its own header. - me.colIdx = colMgr.indexOf(col); - } - } - return me; - }, - /** - * Returns the cell object referenced *at the time of calling*. Note that grid DOM is transient, and - * the cell referenced may be removed from the DOM due to paging or buffered rendering or column or record removal. - * - * @param {Boolean} returnDom Pass `true` to return a DOM object instead of an {@link Ext.dom.Element Element). - * @return {HTMLElement/Ext.dom.Element} The cell referenced by this context. - */ - getCell: function(returnDom) { - return this.view.getCellByPosition(this, returnDom); - }, - /** - * Returns the row object referenced *at the time of calling*. Note that grid DOM is transient, and - * the row referenced may be removed from the DOM due to paging or buffered rendering or column or record removal. - * - * @param {Boolean} returnDom Pass `true` to return a DOM object instead of an {@link Ext.dom.Element Element). - * @return {HTMLElement/Ext.dom.Element} The grid row referenced by this context. - */ - getRow: function(returnDom) { - var result = this.view.getRow(this.record); - return returnDom ? result : Ext.get(result); - }, - /** - * Returns the view node object (the encapsulating element of a data row) referenced *at the time of - * calling*. Note that grid DOM is transient, and the node referenced may be removed from the DOM due - * to paging or buffered rendering or column or record removal. - * - * @param {Boolean} returnDom Pass `true` to return a DOM object instead of an {@link Ext.dom.Element Element). - * @return {HTMLElement/Ext.dom.Element} The grid item referenced by this context. - */ - getNode: function(returnDom) { - var result = this.view.getNode(this.record); - return returnDom ? result : Ext.get(result); - }, - /** - * Compares this CellContext object to another CellContext to see if they refer to the same cell. - * @param {Ext.grid.CellContext} other The CellContext to compare. - * @return {Boolean} `true` if the other cell context references the same cell as this. - */ - isEqual: function(other) { - return (other && other.isCellContext && other.record === this.record && other.column === this.column); - }, - /** - * Creates a clone of this CellContext. - * - * The clone may be retargeted without affecting the reference of this context. - * @return {Ext.grid.CellContext} A copy of this context, referencing the same cell. - */ - clone: function() { - var me = this, - result = new me.self(me.view); - result.rowIdx = me.rowIdx; - result.colIdx = me.colIdx; - result.record = me.record; - result.column = me.column; - return result; - }, - privates: { - isFirstColumn: function() { - var cell = this.getCell(true); - if (cell) { - return !cell.previousSibling; - } - }, - isLastColumn: function() { - var cell = this.getCell(true); - if (cell) { - return !cell.nextSibling; - } - }, - getLastColumnIndex: function() { - var row = this.getRow(true); - if (row) { - return row.lastChild.cellIndex; - } - return -1; - }, - /** - * @private - * Navigates left or right within the current row. - * @param {Number} direction `-1` to go towards the row start or `1` to go towards row end - */ - navigate: function(direction) { - var me = this, - columns = me.view.getVisibleColumnManager().getColumns(); - switch (direction) { - case -1: - do { - if (!me.colIdx) { - me.colIdx = columns.length - 1; - } else { - me.colIdx--; - } - me.setColumn(me.colIdx); - } while (// If we iterate off the start, wrap back to the end. - !me.getCell(true)); - break; - case 1: - do { - if (me.colIdx >= columns.length) { - me.colIdx = 0; - } else { - me.colIdx++; - } - me.setColumn(me.colIdx); - } while (// If we iterate off the end, wrap back to the start. - !me.getCell(true)); - break; - } - } - }, - statics: { - compare: function(c1, c2) { - return c1.rowIdx - c2.rowIdx || c1.colIdx - c2.colIdx; - } - } -}); - -/** - * Internal utility class that provides default configuration for cell editing. - * @private - */ -Ext.define('Ext.grid.CellEditor', { - extend: 'Ext.Editor', - /** - * @property {Boolean} isCellEditor - * @readonly - * `true` in this class to identify an object as an instantiated CellEditor, or subclass thereof. - */ - isCellEditor: true, - alignment: 'l-l!', - hideEl: false, - cls: Ext.baseCSSPrefix + 'small-editor ' + Ext.baseCSSPrefix + 'grid-editor ' + Ext.baseCSSPrefix + 'grid-cell-editor', - treeNodeSelector: '.' + Ext.baseCSSPrefix + 'tree-node-text', - shim: false, - shadow: false, - // Set the grid that owns this editor. - // Called by CellEditing#getEditor - setGrid: function(grid) { - var me = this, - oldGrid = me.grid, - viewListeners; - if (grid !== oldGrid) { - viewListeners = { - beforeitemupdate: me.beforeItemUpdate, - itemupdate: me.onItemUpdate, - scope: me - }; - // Remove previous refresh listener - if (oldGrid) { - oldGrid.getView().un(viewListeners); - } - me.grid = grid; - // On view refresh, we need to copy our DOM into the detached body to prevent it from being garbage collected. - grid.getView().on(viewListeners); - } - }, - beforeViewRefresh: function(view) { - var me = this, - dom = me.el && me.el.dom; - if (dom) { - me.wasAllowBlur = me.allowBlur; - if (me.editing) { - // Clear the Panel's cellFocused flag prior to removing it from the DOM - // This will prevent the Panels onFocusLeave from processing the resulting blurring. - view.cellFocused = false; - // Set the Editor.allowBlur setting so that it does not process the upcoming field blur event and terminate the edit - me.allowBlur = false; - } - // Remove the editor from the view to protect it from annihilation: https://sencha.jira.com/browse/EXTJSIV-11713 - if (dom.parentNode) { - // Set refreshing flag so that onFocusLeave caused by removing a focused element - // does not exit actionableMode - view.refreshing = true; - dom.parentNode.removeChild(dom); - } - } - }, - onViewRefresh: function(view) { - var me = this, - dom = me.el && me.el.dom, - cell, - context = me.context; - if (dom) { - // Update the context with the possibly new contextual data - // (refresh might have been caused by a sort or column move etc) - cell = view.getCellByPosition(context, true); - // If the refresh was caused by eg column removal, the cell will not exist. - // In this case, terminate the edit. - if (!cell) { - me.allowBlur = me.wasAllowBlur; - me.completeEdit(); - Ext.getDetachedBody().dom.appendChild(dom); - return; - } - context.node = view.getNode(context.record); - context.row = view.getRow(context.record); - context.cell = cell; - context.rowIdx = view.indexOf(context.row); - cell.insertBefore(dom, cell.firstChild); - me.boundEl = me.container = Ext.get(cell); - me.realign(true); - // If the view was refreshed while we were editing, replace it. - // On IE, the blur event will fire asynchronously, so we must leave - // allowBlur as false for a very short while longer. - // After which we reset it, and refocus the field. - if (me.editing) { - if (Ext.isIE) { - Ext.defer(function() { - // May have been destroyed immediately after refreshing!? - if (!me.destroyed) { - me.allowBlur = me.wasAllowBlur; - me.field.focus(); - } - }, 10); - } else { - me.allowBlur = me.wasAllowBlur; - me.field.focus(); - } - } - } - }, - beforeItemUpdate: function(record, recordIndex, oldItemDom, columnsToUpdate) { - var me = this, - context = me.context, - l = columnsToUpdate.length, - i; - // If this CellEditor's row is to be updated, we *may* have to restore this editor - // due to cell content possibly being changed. - if (record === context.record) { - for (i = 0; i < l; i++) { - // If the cell is scheduled for update, we definitely will need restoration. - if (columnsToUpdate[i] === context.column) { - me.needsFixOnItemUpdate = true; - me.beforeViewRefresh(context.view); - return; - } - } - } - }, - onItemUpdate: function(record, recordIndex, oldItemDom) { - var view = this.context.view; - if (this.needsFixOnItemUpdate) { - // The refreshing flag was set to indicate to the onFocusLeave listener that it - // should ignore focusleave caused by this Editor blurring. - this.needsFixOnItemUpdate = view.refreshing = false; - this.onViewRefresh(view); - } - }, - startEdit: function(boundEl, value, doFocus) { - this.context = this.editingPlugin.context; - this.callParent([ - boundEl, - value, - doFocus - ]); - }, - /** - * @private - * Shows the editor, end ensures that it is rendered into the correct view - * Hides the grid cell inner element when a cell editor is shown. - */ - onShow: function() { - var me = this, - innerCell = me.boundEl.down(me.context.view.innerSelector); - if (innerCell) { - if (me.isForTree) { - innerCell = innerCell.child(me.treeNodeSelector); - } - innerCell.hide(); - } - me.callParent(arguments); - }, - onFocusEnter: function() { - var context = this.context, - view = context.view; - // Focus restoration after a refresh may require realignment and correction - // of the context because it could have been due to a or filter operation and - // the context may have changed position. - context.node = view.getNode(context.record); - context.row = view.getRow(context.record); - context.cell = context.getCell(true); - context.rowIdx = view.indexOf(context.row); - this.realign(true); - this.callParent(arguments); - // Ensure that hide processing does not throw focus back to the previously focused element. - this.focusEnterEvent = null; - }, - onEditComplete: function(remainVisible) { - // When being asked to process edit completion, if we are not hiding, restore the cell now - if (remainVisible) { - this.restoreCell(); - } - this.callParent(arguments); - }, - /** - * @private - * Shows the grid cell inner element when a cell editor is hidden - */ - onHide: function() { - this.restoreCell(); - this.callParent(arguments); - }, - onSpecialKey: function(field, event) { - var me = this, - key = event.getKey(), - complete = me.completeOnEnter && key === event.ENTER, - cancel = me.cancelOnEsc && key === event.ESC, - view = me.editingPlugin.view; - if (complete || cancel) { - // Do not let the key event bubble into the NavigationModel after we're don processing it. - // We control the navigation action here; we focus the cell. - event.stopEvent(); - // Maintain visibility so that focus doesn't leak. - // We need to direct focusback to the owning cell. - if (complete) { - me.completeEdit(true); - } else if (cancel) { - me.cancelEdit(true); - } - view.getNavigationModel().setPosition(me.context, null, event); - view.ownerGrid.setActionableMode(false); - } - }, - getRefOwner: function() { - return this.column && this.column.getView(); - }, - restoreCell: function() { - var me = this, - innerCell = me.boundEl.down(me.context.view.innerSelector); - if (innerCell) { - if (me.isForTree) { - innerCell = innerCell.child(me.treeNodeSelector); - } - innerCell.show(); - } - }, - /** - * @private - * Fix checkbox blur when it is clicked. - */ - afterRender: function() { - var me = this, - field = me.field; - me.callParent(arguments); - if (field.isCheckbox) { - field.mon(field.inputEl, { - mousedown: me.onCheckBoxMouseDown, - click: me.onCheckBoxClick, - scope: me - }); - } - }, - /** - * @private - * Because when checkbox is clicked it loses focus completeEdit is bypassed. - */ - onCheckBoxMouseDown: function() { - this.completeEdit = Ext.emptyFn; - }, - /** - * @private - * Restore checkbox focus and completeEdit method. - */ - onCheckBoxClick: function() { - delete this.completeEdit; - this.field.focus(false, 10); - }, - /** - * @private - * Realigns the Editor to the grid cell, or to the text node in the grid inner cell - * if the inner cell contains multiple child nodes. - */ - realign: function(autoSize) { - var me = this, - boundEl = me.boundEl, - innerCell = boundEl.down(me.context.view.innerSelector), - innerCellTextNode = innerCell.dom.firstChild, - width = boundEl.getWidth(), - offsets = Ext.Array.clone(me.offsets), - grid = me.grid, - xOffset, - v = '', - // innerCell is empty if there are no children, or there is one text node, and it contains whitespace - isEmpty = !innerCellTextNode || (innerCellTextNode.nodeType === 3 && !(Ext.String.trim(v = innerCellTextNode.data).length)); - if (me.isForTree) { - // When editing a tree, adjust the width and offsets of the editor to line - // up with the tree cell's text element - xOffset = me.getTreeNodeOffset(innerCell); - width -= Math.abs(xOffset); - offsets[0] += xOffset; - } - if (grid.columnLines) { - // Subtract the column border width so that the editor displays inside the - // borders. The column border could be either on the left or the right depending - // on whether the grid is RTL - using the sum of both borders works in both modes. - width -= boundEl.getBorderWidth('rl'); - } - if (autoSize === true) { - me.field.setWidth(width); - } - // https://sencha.jira.com/browse/EXTJSIV-10871 Ensure the data bearing element has a height from text. - if (isEmpty) { - innerCell.dom.innerHTML = 'X'; - } - me.alignTo(boundEl, me.alignment, offsets); - if (isEmpty) { - innerCell.dom.firstChild.data = v; - } - }, - getTreeNodeOffset: function(innerCell) { - return innerCell.child(this.treeNodeSelector).getOffsetsTo(innerCell)[0]; - } -}); - -/** - * Component layout for grid column headers which have a title element at the top followed by content. - * @private - */ -Ext.define('Ext.grid.ColumnComponentLayout', { - extend: 'Ext.layout.component.Auto', - alias: 'layout.columncomponent', - type: 'columncomponent', - setWidthInDom: true, - _paddingReset: { - paddingTop: '', - // reset back to default padding of the style - paddingBottom: '' - }, - columnAutoCls: Ext.baseCSSPrefix + 'column-header-text-container-auto', - beginLayout: function(ownerContext) { - this.callParent(arguments); - ownerContext.titleContext = ownerContext.getEl('titleEl'); - }, - beginLayoutCycle: function(ownerContext) { - var me = this, - owner = me.owner, - shrinkWrap = ownerContext.widthModel.shrinkWrap; - me.callParent(arguments); - // If shrinkwrapping, allow content width to stretch the element - if (shrinkWrap) { - owner.el.setWidth(''); - } - owner.textContainerEl[shrinkWrap && !owner.isGroupHeader ? 'addCls' : 'removeCls'](me.columnAutoCls); - owner.titleEl.setStyle(me._paddingReset); - }, - // If not shrink wrapping, push height info down into child items - publishInnerHeight: function(ownerContext, outerHeight) { - var me = this, - owner = me.owner, - innerHeight; - // TreePanels (and grids with hideHeaders: true) set their column container height to zero to hide them. - // This is because they need to lay out in order to calculate widths for the columns (eg flexes). - // If there is no height to lay out, bail out early. - if (owner.getRootHeaderCt().hiddenHeaders) { - ownerContext.setProp('innerHeight', 0); - return; - } - // If this ia a group header; that is, it contains subheaders... - // hasRawContent = !(target.isContainer && target.items.items.length > 0) - if (!ownerContext.hasRawContent) { - // We do not have enough information to get the height of the titleEl - if (owner.headerWrap && !ownerContext.hasDomProp('width')) { - me.done = false; - return; - } - innerHeight = outerHeight - ownerContext.getBorderInfo().height; - ownerContext.setProp('innerHeight', innerHeight - owner.titleEl.getHeight(), false); - } - }, - // We do not need the Direct2D sub pixel measurement here. Just the offsetHeight will do. - // TODO: When https://sencha.jira.com/browse/EXTJSIV-7734 is fixed to not do subpixel adjustment on height, - // remove this override. - measureContentHeight: function(ownerContext) { - return ownerContext.el.dom.offsetHeight; - }, - // If not shrink wrapping, push width info down into child items - publishInnerWidth: function(ownerContext, outerWidth) { - // If we are acting as a container, publish the innerWidth for the ColumnLayout to use - if (!ownerContext.hasRawContent) { - ownerContext.setProp('innerWidth', outerWidth - ownerContext.getBorderInfo().width, false); - } - }, - // Push content height outwards when we are shrinkwrapping - calculateOwnerHeightFromContentHeight: function(ownerContext, contentHeight) { - var result = this.callParent(arguments), - owner = this.owner; - // If we are NOT a group header, we just use the auto component's measurement - if (!ownerContext.hasRawContent) { - if (!owner.headerWrap || ownerContext.hasDomProp('width')) { - return contentHeight + owner.titleEl.getHeight() + ownerContext.getBorderInfo().height; - } - // We do not have the information to return the height yet because we cannot know - // the final height of the text el - return null; - } - return result; - }, - // Push content width outwards when we are shrinkwrapping - calculateOwnerWidthFromContentWidth: function(ownerContext, contentWidth) { - var owner = this.owner, - padWidth = ownerContext.getPaddingInfo().width, - triggerOffset = this.getTriggerOffset(owner, ownerContext), - inner; - // Only measure the content if we're not grouped, otherwise - // the size should be governed by the children - if (owner.isGroupHeader) { - inner = contentWidth; - } else { - inner = Math.max(contentWidth, owner.textEl.getWidth() + ownerContext.titleContext.getPaddingInfo().width); - } - return inner + padWidth + triggerOffset; - }, - getTriggerOffset: function(owner, ownerContext) { - var width = 0; - if (ownerContext.widthModel.shrinkWrap && !owner.menuDisabled) { - // If we have any children underneath, then we already have space reserved - if (owner.query('>:not([hidden])').length === 0) { - width = owner.getTriggerElWidth(); - } - } - return width; - } -}); - -/** - * This is a base class for layouts that contain a single item that automatically expands to fill the layout's - * container. This class is intended to be extended or created via the layout:'fit' - * {@link Ext.container.Container#layout} config, and should generally not need to be created directly via the new keyword. - * - * Fit layout does not have any direct config options (other than inherited ones). To fit a panel to a container using - * Fit layout, simply set `layout: 'fit'` on the container and add a single panel to it. - * - * @example - * Ext.create('Ext.panel.Panel', { - * title: 'Fit Layout', - * width: 300, - * height: 150, - * layout:'fit', - * items: { - * title: 'Inner Panel', - * html: 'This is the inner panel content', - * bodyPadding: 20, - * border: false - * }, - * renderTo: Ext.getBody() - * }); - * - * If the container has multiple items, all of the items will all be equally sized. This is usually not - * desired, so to avoid this, place only a **single** item in the container. This sizing of all items - * can be used to provide a background {@link Ext.Img image} that is "behind" another item - * such as a {@link Ext.view.View dataview} if you also absolutely position the items. - */ -Ext.define('Ext.layout.container.Fit', { - /* Begin Definitions */ - extend: 'Ext.layout.container.Container', - alternateClassName: 'Ext.layout.FitLayout', - alias: 'layout.fit', - /* End Definitions */ - /** - * @inheritdoc Ext.layout.container.Container#cfg-itemCls - */ - itemCls: Ext.baseCSSPrefix + 'fit-item', - type: 'fit', - manageMargins: true, - sizePolicies: { - 0: { - readsWidth: 1, - readsHeight: 1, - setsWidth: 0, - setsHeight: 0 - }, - 1: { - readsWidth: 0, - readsHeight: 1, - setsWidth: 1, - setsHeight: 0 - }, - 2: { - readsWidth: 1, - readsHeight: 0, - setsWidth: 0, - setsHeight: 1 - }, - 3: { - readsWidth: 0, - readsHeight: 0, - setsWidth: 1, - setsHeight: 1 - } - }, - getItemSizePolicy: function(item, ownerSizeModel) { - // this layout's sizePolicy is derived from its owner's sizeModel: - var sizeModel = ownerSizeModel || this.owner.getSizeModel(), - mode = (sizeModel.width.shrinkWrap ? 0 : 1) | // jshint ignore:line - (sizeModel.height.shrinkWrap ? 0 : 2); - return this.sizePolicies[mode]; - }, - beginLayoutCycle: function(ownerContext, firstCycle) { - var me = this, - // determine these before the lastSizeModels get updated: - resetHeight = me.lastHeightModel && me.lastHeightModel.calculated, - resetWidth = me.lastWidthModel && me.lastWidthModel.calculated, - resetSizes = resetWidth || resetHeight, - maxChildMinHeight = 0, - maxChildMinWidth = 0, - c, childItems, i, item, length, margins, minHeight, minWidth, style, undef; - me.callParent(arguments); - // Clear any dimensions which we set before calculation, in case the current - // settings affect the available size. This particularly effects self-sizing - // containers such as fields, in which the target element is naturally sized, - // and should not be stretched by a sized child item. - if (resetSizes && ownerContext.targetContext.el.dom.tagName.toUpperCase() !== 'TD') { - resetSizes = resetWidth = resetHeight = false; - } - childItems = ownerContext.childItems; - length = childItems.length; - for (i = 0; i < length; ++i) { - item = childItems[i]; - // On the firstCycle, we determine the max of the minWidth/Height of the items - // since these can cause the container to grow scrollbars despite our attempts - // to fit the child to the container. - if (firstCycle) { - c = item.target; - minHeight = c.minHeight; - minWidth = c.minWidth; - if (minWidth || minHeight) { - margins = item.marginInfo || item.getMarginInfo(); - // if the child item has undefined minWidth/Height, these will become - // NaN by adding the margins... - minHeight += margins.height; - minWidth += margins.height; - // if the child item has undefined minWidth/Height, these comparisons - // will evaluate to false... that is, "0 < NaN" == false... - if (maxChildMinHeight < minHeight) { - maxChildMinHeight = minHeight; - } - if (maxChildMinWidth < minWidth) { - maxChildMinWidth = minWidth; - } - } - } - if (resetSizes) { - style = item.el.dom.style; - if (resetHeight) { - style.height = ''; - } - if (resetWidth) { - style.width = ''; - } - } - } - if (firstCycle) { - ownerContext.maxChildMinHeight = maxChildMinHeight; - ownerContext.maxChildMinWidth = maxChildMinWidth; - } - // Cache the overflowX/Y flags, but make them false in shrinkWrap mode (since we - // won't be triggering overflow in that case) and false if we have no minSize (so - // no child to trigger an overflow). - c = ownerContext.target; - ownerContext.overflowX = (!ownerContext.widthModel.shrinkWrap && ownerContext.maxChildMinWidth && c.scrollFlags.x) || undef; - ownerContext.overflowY = (!ownerContext.heightModel.shrinkWrap && ownerContext.maxChildMinHeight && c.scrollFlags.y) || undef; - }, - calculate: function(ownerContext) { - var me = this, - childItems = ownerContext.childItems, - length = childItems.length, - containerSize = me.getContainerSize(ownerContext), - info = { - length: length, - ownerContext: ownerContext, - targetSize: containerSize - }, - shrinkWrapWidth = ownerContext.widthModel.shrinkWrap, - shrinkWrapHeight = ownerContext.heightModel.shrinkWrap, - overflowX = ownerContext.overflowX, - overflowY = ownerContext.overflowY, - scrollbars, scrollbarSize, padding, i, contentWidth, contentHeight; - ownerContext.state.info = info; - if (overflowX || overflowY) { - // If we have children that have minHeight/Width, we may be forced to overflow - // and gain scrollbars. If so, we want to remove their space from the other - // axis so that we fit things inside the scrollbars rather than under them. - scrollbars = me.getScrollbarsNeeded(overflowX && containerSize.width, overflowY && containerSize.height, ownerContext.maxChildMinWidth, ownerContext.maxChildMinHeight); - if (scrollbars) { - scrollbarSize = Ext.getScrollbarSize(); - if (scrollbars & 1) { - // jshint ignore:line - // if we need the hscrollbar, remove its height - containerSize.height -= scrollbarSize.height; - } - if (scrollbars & 2) { - // jshint ignore:line - // if we need the vscrollbar, remove its width - containerSize.width -= scrollbarSize.width; - } - } - } - // If length === 0, it means we either have no child items, or the children are hidden - if (length > 0) { - // Size the child items to the container (if non-shrinkWrap): - for (i = 0; i < length; ++i) { - info.index = i; - me.fitItem(childItems[i], info); - } - } else { - info.contentWidth = info.contentHeight = 0; - } - if (shrinkWrapHeight || shrinkWrapWidth) { - padding = ownerContext.targetContext.getPaddingInfo(); - if (shrinkWrapWidth) { - if (overflowY && !containerSize.gotHeight) { - // if we might overflow vertically and don't have the container height, - // we don't know if we will need a vscrollbar or not, so we must wait - // for that height so that we can determine the contentWidth... - me.done = false; - } else { - contentWidth = info.contentWidth + padding.width; - // the scrollbar flag (if set) will indicate that an overflow exists on - // the horz(1) or vert(2) axis... if not set, then there could never be - // an overflow... - if (scrollbars & 2) { - // jshint ignore:line - // if we need the vscrollbar, add its width - contentWidth += scrollbarSize.width; - } - if (!ownerContext.setContentWidth(contentWidth)) { - me.done = false; - } - } - } - if (shrinkWrapHeight) { - if (overflowX && !containerSize.gotWidth) { - // if we might overflow horizontally and don't have the container width, - // we don't know if we will need a hscrollbar or not, so we must wait - // for that width so that we can determine the contentHeight... - me.done = false; - } else { - contentHeight = info.contentHeight + padding.height; - // the scrollbar flag (if set) will indicate that an overflow exists on - // the horz(1) or vert(2) axis... if not set, then there could never be - // an overflow... - if (scrollbars & 1) { - // jshint ignore:line - // if we need the hscrollbar, add its height - contentHeight += scrollbarSize.height; - } - if (!ownerContext.setContentHeight(contentHeight)) { - me.done = false; - } - } - } - } - }, - fitItem: function(itemContext, info) { - var me = this; - if (itemContext.invalid) { - me.done = false; - return; - } - info.margins = itemContext.getMarginInfo(); - info.needed = info.got = 0; - me.fitItemWidth(itemContext, info); - me.fitItemHeight(itemContext, info); - // If not all required dimensions have been satisfied, we're not done. - if (info.got !== info.needed) { - me.done = false; - } - }, - fitItemWidth: function(itemContext, info) { - var contentWidth, width; - // Attempt to set only dimensions that are being controlled, not shrinkWrap dimensions - if (info.ownerContext.widthModel.shrinkWrap) { - // contentWidth must include the margins to be consistent with setItemWidth - width = itemContext.getProp('width') + info.margins.width; - // because we add margins, width will be NaN or a number (not undefined) - contentWidth = info.contentWidth; - if (contentWidth === undefined) { - info.contentWidth = width; - } else { - info.contentWidth = Math.max(contentWidth, width); - } - } else if (itemContext.widthModel.calculated) { - ++info.needed; - if (info.targetSize.gotWidth) { - ++info.got; - this.setItemWidth(itemContext, info); - } else { - // Too early to position - return; - } - } - this.positionItemX(itemContext, info); - }, - fitItemHeight: function(itemContext, info) { - var contentHeight, height; - if (info.ownerContext.heightModel.shrinkWrap) { - // contentHeight must include the margins to be consistent with setItemHeight - height = itemContext.getProp('height') + info.margins.height; - // because we add margins, height will be NaN or a number (not undefined) - contentHeight = info.contentHeight; - if (contentHeight === undefined) { - info.contentHeight = height; - } else { - info.contentHeight = Math.max(contentHeight, height); - } - } else if (itemContext.heightModel.calculated) { - ++info.needed; - if (info.targetSize.gotHeight) { - ++info.got; - this.setItemHeight(itemContext, info); - } else { - // Too early to position - return; - } - } - this.positionItemY(itemContext, info); - }, - positionItemX: function(itemContext, info) { - var margins = info.margins; - // Adjust position to account for configured margins or if we have multiple items - // (all items should overlap): - if (info.index || margins.left) { - itemContext.setProp('x', margins.left); - } - if (margins.width && info.ownerContext.widthModel.shrinkWrap) { - // Need the margins for shrink-wrapping but old IE sometimes collapses the left margin into the padding - itemContext.setProp('margin-right', margins.width); - } - }, - positionItemY: function(itemContext, info) { - var margins = info.margins; - if (info.index || margins.top) { - itemContext.setProp('y', margins.top); - } - if (margins.height && info.ownerContext.heightModel.shrinkWrap) { - // Need the margins for shrink-wrapping but old IE sometimes collapses the top margin into the padding - itemContext.setProp('margin-bottom', margins.height); - } - }, - setItemHeight: function(itemContext, info) { - itemContext.setHeight(info.targetSize.height - info.margins.height); - }, - setItemWidth: function(itemContext, info) { - itemContext.setWidth(info.targetSize.width - info.margins.width); - } -}); - -/** - * This class is the base class for both {@link Ext.tree.Panel TreePanel} and - * {@link Ext.grid.Panel GridPanel}. - * - * TablePanel aggregates: - * - * - a Selection Model - * - a View - * - a Store - * - Ext.grid.header.Container - * - * @mixins Ext.grid.locking.Lockable - */ -Ext.define('Ext.panel.Table', { - extend: 'Ext.panel.Panel', - alias: 'widget.tablepanel', - requires: [ - 'Ext.layout.container.Fit' - ], - uses: [ - 'Ext.selection.RowModel', - 'Ext.selection.CellModel', - 'Ext.selection.CheckboxModel', - 'Ext.grid.plugin.BufferedRenderer', - 'Ext.grid.header.Container', - 'Ext.grid.locking.Lockable', - 'Ext.grid.NavigationModel' - ], - extraBaseCls: Ext.baseCSSPrefix + 'grid', - extraBodyCls: Ext.baseCSSPrefix + 'grid-body', - actionableModeCls: Ext.baseCSSPrefix + 'grid-actionable', - noHeaderBordersCls: Ext.baseCSSPrefix + 'no-header-borders', - defaultBindProperty: 'store', - layout: 'fit', - ariaRole: 'grid', - config: { - /** - * @cfg {Ext.data.Model} selection - * The selected model. Typically used with {@link #bind binding}. - */ - selection: null, - /** - * @cfg {Boolean} [headerBorders=`true`] - * To show no borders around grid headers, configure this as `false`. - */ - headerBorders: true - }, - publishes: [ - 'selection' - ], - twoWayBindable: [ - 'selection' - ], - /** - * @cfg {Boolean} [autoLoad=false] - * Use `true` to load the store as soon as this component is fully constructed. It is - * best to initiate the store load this way to allow this component and potentially - * its plugins (such as `{@link Ext.grid.filters.Filters}`) to be ready to load. - */ - autoLoad: false, - /** - * @cfg {Boolean} [variableRowHeight=false] - * @deprecated 5.0.0 Use {@link Ext.grid.column.Column#variableRowHeight} instead. - * Configure as `true` if the row heights are not all the same height as the first row. - */ - variableRowHeight: false, - /** - * @cfg {Number} [numFromEdge] - * This configures the zone which causes new rows to be appended to the view. As soon as the edge - * of the rendered grid is this number of rows from the edge of the viewport, the view is moved. - */ - numFromEdge: 2, - /** - * @cfg {Number} [trailingBufferZone] - * TableViews are buffer rendered in 5.x and above which means that only the visible subset of data rows - * are rendered into the DOM. These are removed and added as scrolling demands. - * - * This configures the number of extra rows to render on the trailing side of scrolling - * **outside the {@link #numFromEdge}** buffer as scrolling proceeds. - */ - trailingBufferZone: 10, - /** - * @cfg {Number} [leadingBufferZone] - * TableViews are buffer rendered in 5.x and above which means that only the visible subset of data rows - * are rendered into the DOM. These are removed and added as scrolling demands. - * - * This configures the number of extra rows to render on the leading side of scrolling - * **outside the {@link #numFromEdge}** buffer as scrolling proceeds. - */ - leadingBufferZone: 20, - /** - * @property {Boolean} hasView - * True to indicate that a view has been injected into the panel. - */ - hasView: false, - /** - * @property items - * @hide - */ - /** - * @cfg {String} viewType - * An xtype of view to use. This is automatically set to 'tableview' by {@link Ext.grid.Panel Grid} - * and to 'treeview' by {@link Ext.tree.Panel Tree}. - * @protected - */ - viewType: null, - /** - * @cfg {Object} viewConfig - * A config object that will be applied to the grid's UI view. Any of the config options available for - * {@link Ext.view.Table} can be specified here. This option is ignored if {@link #view} is specified. - */ - /** - * @cfg {Ext.view.Table} view - * The {@link Ext.view.Table} used by the grid. Use {@link #viewConfig} to just supply some config options to - * view (instead of creating an entire View instance). - */ - /** - * @cfg {String} [selType] - * An xtype of selection model to use. This is used to create selection model if just - * a config object or nothing at all given in {@link #selModel} config. - * - * @deprecated 5.1.0 Use the {@link #selModel}'s `type` property. Or, if no other - * configs are required, use the string form of selModel. - */ - /** - * @cfg {Ext.selection.Model/Object/String} [selModel=rowmodel] - * A {@link Ext.selection.Model selection model} instance or config object, or the selection model class's alias string. - * - * In latter case its `type` property determines to which type of selection model this config is applied. - */ - /** - * @cfg {Boolean} [multiSelect=false] - * True to enable 'MULTI' selection mode on selection model. - * @deprecated 4.1.1 Use {@link Ext.selection.Model#mode} 'MULTI' instead. - */ - /** - * @cfg {Boolean} [simpleSelect=false] - * True to enable 'SIMPLE' selection mode on selection model. - * @deprecated 4.1.1 Use {@link Ext.selection.Model#mode} 'SIMPLE' instead. - */ - /** - * @cfg {Ext.data.Store/String/Object} store (required) - * The data source to which the grid / tree is bound. Acceptable values for this - * property are: - * - * - **any {@link Ext.data.Store Store} class / subclass** - * - **an {@link Ext.data.Store#storeId ID of a store}** - * - **a {@link Ext.data.Store Store} config object**. When passing a config you can - * specify the store type by alias. Passing a config object with a store type will - * dynamically create a new store of that type when the grid / tree is instantiated. - * - * For example: - * - * Ext.define('MyApp.store.Customers', { - * extend: 'Ext.data.Store', - * alias: 'store.customerstore', - * fields: ['name'] - * }); - * - * Ext.create({ - * xtype: 'gridpanel', - * renderTo: document.body, - * store: { - * type: 'customerstore', - * data: [{ - * name: 'Foo' - * }] - * }, - * columns: [{ - * text: 'Name', - * dataIndex: 'name' - * }] - * }); - */ - /** - * @cfg {String/Boolean} scroll - * Scrollers configuration. Valid values are 'both', 'horizontal' or 'vertical'. - * True implies 'both'. False implies 'none'. - * @deprecated 5.1.0 Use {@link #scrollable} instead - */ - /** - * @cfg {Boolean} [reserveScrollbar=false] - * Set this to true to **always** leave a scrollbar sized space at the end of the grid content when - * fitting content into the width of the grid. - * - * If the grid's record count fluctuates enough to hide and show the scrollbar regularly, this setting - * avoids the multiple layouts associated with switching from scrollbar present to scrollbar not present. - */ - /** - * @cfg {Ext.grid.column.Column[]/Object} columns - * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this - * grid. Each column definition provides the header text for the column, and a definition of where the data for that - * column comes from. - * - * This can also be a configuration object for a {@link Ext.grid.header.Container HeaderContainer} which may override - * certain default configurations if necessary. For example, the special layout may be overridden to use a simpler - * layout, or one can set default values shared by all columns: - * - * columns: { - * items: [ - * { - * text: "Column A", - * dataIndex: "field_A" - * },{ - * text: "Column B", - * dataIndex: "field_B" - * }, - * ... - * ], - * defaults: { - * flex: 1 - * } - * } - */ - /** - * @cfg {Boolean} forceFit - * True to force the columns to fit into the available width. Headers are first sized according to configuration, - * whether that be a specific width, or flex. Then they are all proportionally changed in width so that the entire - * content width is used. For more accurate control, it is more optimal to specify a flex setting on the columns - * that are to be stretched & explicit widths on columns that are not. - */ - /** - * @cfg {Ext.grid.feature.Feature[]/Object[]/Ext.enums.Feature[]} features - * An array of grid Features to be added to this grid. Can also be just a single feature instead of array. - * - * Features config behaves much like {@link #plugins}. - * A feature can be added by either directly referencing the instance: - * - * features: [Ext.create('Ext.grid.feature.GroupingSummary', {groupHeaderTpl: 'Subject: {name}'})], - * - * By using config object with ftype: - * - * features: [{ftype: 'groupingsummary', groupHeaderTpl: 'Subject: {name}'}], - * - * Or with just a ftype: - * - * features: ['grouping', 'groupingsummary'], - * - * See {@link Ext.enums.Feature} for list of all ftypes. - */ - /** - * @cfg {Boolean} [hideHeaders=false] - * True to hide column headers. - */ - /** - * @cfg {Boolean} [deferRowRender=false] - * Configure as `true` to enable deferred row rendering. - * - * This allows the View to execute a refresh quickly, with the update of the row structure deferred so - * that layouts with GridPanels appear, and lay out more quickly. - */ - deferRowRender: false, - /** - * @cfg {Boolean} [sortableColumns=true] - * False to disable column sorting via clicking the header and via the Sorting menu items. - */ - sortableColumns: true, - /** - * @cfg {Boolean} [multiColumnSort=false] - * Configure as `true` to have columns remember their sorted state after other columns have been clicked upon to sort. - * - * As subsequent columns are clicked upon, they become the new primary sort key. - * - * The maximum number of sorters allowed in a Store is configurable via its underlying data collection. See {@link Ext.util.Collection#multiSortLimit} - */ - multiColumnSort: false, - /** - * @cfg {Boolean} [enableLocking=false] - * Configure as `true` to enable locking support for this grid. Alternatively, locking will also be automatically - * enabled if any of the columns in the {@link #columns columns} configuration contain a {@link Ext.grid.column.Column#locked locked} config option. - * - * A locking grid is processed in a special way. The configuration options are cloned and *two* grids are created to be the locked (left) side - * and the normal (right) side. This Panel becomes merely a {@link Ext.container.Container container} which arranges both in an {@link Ext.layout.container.HBox HBox} layout. - * - * {@link #plugins Plugins} may be targeted at either locked, or unlocked grid, or, both, in which case the plugin is cloned and used on both sides. - * - * Plugins may also be targeted at the containing locking Panel. - * - * This is configured by specifying a `lockableScope` property in your plugin which may have the following values: - * - * * `"both"` (the default) - The plugin is added to both grids - * * `"top"` - The plugin is added to the containing Panel - * * `"locked"` - The plugin is added to the locked (left) grid - * * `"normal"` - The plugin is added to the normal (right) grid - * - * If `both` is specified, then each copy of the plugin gains a property `lockingPartner` which references its sibling on the other side so that they - * can synchronize operations is necessary. - * - * {@link #features Features} may also be configured with `lockableScope` and may target the locked grid, the normal grid or both grids. Features - * also get a `lockingPartner` reference injected. - */ - enableLocking: false, - /** - * @private - * Used to determine where to go down to find views - * this is here to support locking. - */ - scrollerOwner: true, - /** - * @cfg {Boolean} [enableColumnMove=true] - * False to disable column dragging within this grid. - */ - enableColumnMove: true, - /** - * @cfg {Boolean} [sealedColumns=false] - * True to constrain column dragging so that a column cannot be dragged in or out of it's - * current group. Only relevant while {@link #enableColumnMove} is enabled. - */ - sealedColumns: false, - /** - * @cfg {Boolean} [enableColumnResize=true] - * False to disable column resizing within this grid. - */ - enableColumnResize: true, - /** - * @cfg {Boolean} [enableColumnHide=true] - * False to disable column hiding within this grid. - */ - /** - * @cfg {Boolean} columnLines Adds column line styling - */ - /** - * @cfg {Boolean} [rowLines=true] Adds row line styling - */ - rowLines: true, - /** - * @cfg {Boolean} [disableSelection=false] - * True to disable selection model. - */ - /** - * @cfg {String} emptyText Default text (HTML tags are accepted) to display in the - * Panel body when the Store is empty. When specified, and the Store is empty, the - * text will be rendered inside a DIV with the CSS class "x-grid-empty". The emptyText - * will not display until the first load of the associated store by default. If you - * want the text to be displayed prior to the first store load use the - * {@link Ext.view.Table#deferEmptyText deferEmptyText} config in the {@link #viewConfig} config. - */ - /** - * @cfg {Boolean} [allowDeselect=false] - * True to allow deselecting a record. This config is forwarded to {@link Ext.selection.Model#allowDeselect}. - */ - /** - * @cfg {Boolean} [bufferedRenderer=true] - * Buffered rendering is enabled by default. - * - * Configure as `false` to disable buffered rendering. See {@link Ext.grid.plugin.BufferedRenderer}. - * - * @since 5.0.0 - */ - bufferedRenderer: true, - /** - * @cfg stateEvents - * @inheritdoc Ext.state.Stateful#cfg-stateEvents - * @localdoc By default the following stateEvents are added: - * - * - {@link #event-resize} - _(added by Ext.Component)_ - * - {@link #event-collapse} - _(added by Ext.panel.Panel)_ - * - {@link #event-expand} - _(added by Ext.panel.Panel)_ - * - {@link #event-columnresize} - * - {@link #event-columnmove} - * - {@link #event-columnhide} - * - {@link #event-columnshow} - * - {@link #event-sortchange} - * - {@link #event-filterchange} - * - {@link #event-groupchange} - */ - /** - * @property {Boolean} optimizedColumnMove - * If you are writing a grid plugin or a {Ext.grid.feature.Feature Feature} which creates a column-based structure which - * needs a view refresh when columns are moved, then set this property in the grid. - * - * An example is the built in {@link Ext.grid.feature.AbstractSummary Summary} Feature. This creates summary rows, and the - * summary columns must be in the same order as the data columns. This plugin sets the `optimizedColumnMove` to `false. - */ - /** - * @property {Ext.panel.Table} ownerGrid - * A reference to the top-level owning grid component. - * - * This is a reference to this GridPanel if this GridPanel is not part of a locked grid arrangement. - * @readonly - * @private - * @since 5.0.0 - */ - ownerGrid: null, - colLinesCls: Ext.baseCSSPrefix + 'grid-with-col-lines', - rowLinesCls: Ext.baseCSSPrefix + 'grid-with-row-lines', - noRowLinesCls: Ext.baseCSSPrefix + 'grid-no-row-lines', - hiddenHeaderCtCls: Ext.baseCSSPrefix + 'grid-header-ct-hidden', - hiddenHeaderCls: Ext.baseCSSPrefix + 'grid-header-hidden', - resizeMarkerCls: Ext.baseCSSPrefix + 'grid-resize-marker', - emptyCls: Ext.baseCSSPrefix + 'grid-empty', - // The TablePanel claims to be focusable, but it does not place a tabIndex - // on any of its elements. - // Its focus implementation delegates to its view. TableViews are focusable. - focusable: true, - /** - * @event viewready - * Fires when the grid view is available (use this for selecting a default row). - * @param {Ext.panel.Table} this - */ - constructor: function(config) { - var me = this, - topGrid = config && config.ownerGrid, - store; - me.ownerGrid = topGrid || me; - /** - * @property {Array} actionables An array of objects which register themselves with a grid panel using - * {@link #registerActionable} which are consulted upon entry into actionable mode. - * - * These must implement the following methods: - * - * - activateCell Called when actionable mode is requested upon a cell. A {@link Ext.grid.CellContext CellContext} - * object is passed. If that cell is actionable by the terms of the callee, the callee should return `true` if it - * ascertains that the cell is actionable, and that it now contains focusable elements which may be tabbed to. - * - activateRow Called when the user enters actionable mode in a row. The row DOM is passed. Actionables - * should take any action they need to prime the row for cell activation which happens as users TAB from cell to cell. - * @readonly - */ - me.actionables = topGrid ? topGrid.actionables : []; - // One shared array when there's a lockable at the top - me.callParent([ - config - ]); - store = me.store; - // Any further changes become stateful. - store.trackStateChanges = true; - if (me.autoLoad) { - // Note: if there is a store bound by a VM, we (might) do the load in #setStore. - if (!store.isEmptyStore) { - store.load(); - } - } - }, - /** - * - * @param {Object} actionable An object which has an interest in the implementation of actionable mode in - * this grid. - * - * An actionable object may be a Plugin which upon activation injects tabbable elements or Components into - * a grid row. - */ - registerActionable: function(actionable) { - // If a lockableScope: 'both' plugin/feature registers on each side, only include it in the actionables once. - Ext.Array.include(this.actionables, actionable); - }, - initComponent: function() { - if (this.verticalScroller) { - Ext.raise("The verticalScroller config is not supported."); - } - if (!this.viewType) { - Ext.raise("You must specify a viewType config."); - } - if (this.headers) { - Ext.raise("The headers config is not supported. Please specify columns instead."); - } - var me = this, - headerCtCfg = me.columns || me.colModel || [], - store, view, i, len, bufferedRenderer, columns, viewScroller, headerCt; - // Look up the configured Store. If none configured, use the fieldless, empty Store - // defined in Ext.data.Store. - store = me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store'); - me.enableLocking = me.enableLocking || me.hasLockedColumns(headerCtCfg); - // Construct the plugins now rather than in the constructor of AbstractComponent because the component may have a subclass - // that has overridden initComponent and defined plugins in it. For plugins like RowExpander that rely upon a grid feature, - // this is a problem because the view needs to know about all its features before it's constructed. Constructing the plugins - // now ensures that plugins defined in the instance config or in initComponent are all constructed before the view. - // See EXTJSIV-11927. - // - // Note that any components that do not inherit from this class will still have their plugins constructed in - // AbstractComponent:initComponent. - if (me.plugins) { - me.plugins = me.constructPlugins(); - } - // Add the row/column line classes to the body element so that the settings are not inherited by docked grids (https://sencha.jira.com/browse/EXTJSIV-9263). - if (me.columnLines) { - me.addBodyCls(me.colLinesCls); - } - me.addBodyCls(me.rowLines ? me.rowLinesCls : me.noRowLinesCls); - me.addBodyCls(me.extraBodyCls); - // If any of the Column objects contain a locked property, and are not processed, this is a lockable TablePanel, a - // special view will be injected by the Ext.grid.locking.Lockable mixin, so no processing of . - if (me.enableLocking) { - me.self.mixin('lockable', Ext.grid.locking.Lockable); - me.injectLockable(); - headerCt = me.headerCt; - } else // Not lockable - create the HeaderContainer - { - // It's a fully instantiated HeaderContainer - if (headerCtCfg.isRootHeader) { - if (me.hideHeaders) { - headerCtCfg.setHeight(0); - // don't set the hidden property, we still need these to layout - headerCtCfg.hiddenHeaders = true; - } else { - // the header container is not user scrollable, but it has a scroller instance - // so that we can sync its scroll position with that of the grid view - headerCtCfg.setScrollable({ - x: false, - y: false - }); - } - me.headerCt = headerCt = headerCtCfg; - headerCt.grid = me; - headerCt.forceFit = !!me.forceFit; - headerCt.$initParent = me; - // If it's an instance then the column managers were already created and bound to the headerCt. - me.columnManager = headerCtCfg.columnManager; - me.visibleColumnManager = headerCtCfg.visibleColumnManager; - } else // It's an array of Column definitions, or a config object of a HeaderContainer - { - if (Ext.isArray(headerCtCfg)) { - headerCtCfg = { - items: headerCtCfg - }; - } - Ext.apply(headerCtCfg, { - grid: me, - $initParent: me, - forceFit: me.forceFit, - sortable: me.sortableColumns, - enableColumnMove: me.enableColumnMove, - enableColumnResize: me.enableColumnResize, - columnLines: me.columnLines, - sealed: me.sealedColumns, - // the header container is not user scrollable, but if it is visible, - // it has a scroller instance so that we can sync its scroll position with that of the grid view - scrollable: me.hideHeaders ? undefined : { - x: false, - y: false - } - }); - if (me.hideHeaders) { - headerCtCfg.height = 0; - // don't set the hidden property, we still need these to layout - headerCtCfg.hiddenHeaders = true; - } - if (Ext.isDefined(me.enableColumnHide)) { - headerCtCfg.enableColumnHide = me.enableColumnHide; - } - me.headerCt = headerCt = new Ext.grid.header.Container(headerCtCfg); - } - } - // Maintain backward compatibiliy by providing the initial leaf column set as a property. - me.columns = columns = headerCt.getGridColumns(); - me.scrollTask = new Ext.util.DelayedTask(me.syncHorizontalScroll, me); - me.cls = (me.cls || '') + (' ' + me.extraBaseCls); - // autoScroll is not a valid configuration - delete me.autoScroll; - bufferedRenderer = me.plugins && Ext.Array.findBy(me.plugins, function(p) { - return p.isBufferedRenderer; - }); - // If we find one in the plugins, just use that. - if (bufferedRenderer) { - me.bufferedRenderer = bufferedRenderer; - } - // If this TablePanel is lockable (Either configured lockable, or any of the defined columns has a 'locked' property) - // then a special lockable view containing 2 side-by-side grids will have been injected so we do not need to set up any UI. - if (!me.hasView) { - // If the Store is paging blocks of the dataset in, then it can only be sorted remotely. - if (store.isBufferedStore && !store.getRemoteSort()) { - for (i = 0 , len = columns.length; i < len; i++) { - columns[i].sortable = false; - } - } - if (me.hideHeaders) { - me.headerCt.addCls(me.hiddenHeaderCtCls); - me.addCls(me.hiddenHeaderCls); - } - me.relayHeaderCtEvents(headerCt); - me.features = me.features || []; - if (!Ext.isArray(me.features)) { - me.features = [ - me.features - ]; - } - me.dockedItems = [].concat(me.dockedItems || []); - me.dockedItems.unshift(headerCt); - me.viewConfig = me.viewConfig || {}; - // AbstractDataView will look up a Store configured as an object - // getView converts viewConfig into a View instance - view = me.getView(); - me.items = [ - view - ]; - me.hasView = true; - // Add a listener to synchronize the horizontal scroll position of the headers - // with the table view's element... Unless we are not showing headers! - if (!me.hideHeaders) { - // sync the horizontal scroll position of the headerCt as the view is scrolled. - viewScroller = view.getScrollable(); - if (viewScroller) { - headerCt.getScrollable().addPartner(viewScroller, 'x'); - } - } - // Attach this Panel to the Store - me.bindStore(store, true); - me.mon(view, { - viewready: me.onViewReady, - refresh: me.onRestoreHorzScroll, - scope: me - }); - } - // Whatever kind of View we have, be it a TableView, or a LockingView, we are interested in the selection model - me.selModel = me.view.getSelectionModel(); - if (me.selModel.isRowModel) { - me.selModel.on({ - scope: me, - lastselectedchanged: me.updateBindSelection, - selectionchange: me.updateBindSelection - }); - } - // Relay events from the View whether it be a LockingView, or a regular GridView - me.relayEvents(me.view, [ - /** - * @event beforeitemmousedown - * @inheritdoc Ext.view.View#beforeitemmousedown - */ - 'beforeitemmousedown', - /** - * @event beforeitemmouseup - * @inheritdoc Ext.view.View#beforeitemmouseup - */ - 'beforeitemmouseup', - /** - * @event beforeitemmouseenter - * @inheritdoc Ext.view.View#beforeitemmouseenter - */ - 'beforeitemmouseenter', - /** - * @event beforeitemmouseleave - * @inheritdoc Ext.view.View#beforeitemmouseleave - */ - 'beforeitemmouseleave', - /** - * @event beforeitemclick - * @inheritdoc Ext.view.View#beforeitemclick - */ - 'beforeitemclick', - /** - * @event beforeitemdblclick - * @inheritdoc Ext.view.View#beforeitemdblclick - */ - 'beforeitemdblclick', - /** - * @event beforeitemcontextmenu - * @inheritdoc Ext.view.View#beforeitemcontextmenu - */ - 'beforeitemcontextmenu', - /** - * @event itemmousedown - * @inheritdoc Ext.view.View#itemmousedown - */ - 'itemmousedown', - /** - * @event itemmouseup - * @inheritdoc Ext.view.View#itemmouseup - */ - 'itemmouseup', - /** - * @event itemmouseenter - * @inheritdoc Ext.view.View#itemmouseenter - */ - 'itemmouseenter', - /** - * @event itemmouseleave - * @inheritdoc Ext.view.View#itemmouseleave - */ - 'itemmouseleave', - /** - * @event itemclick - * @inheritdoc Ext.view.View#itemclick - */ - 'itemclick', - /** - * @event itemdblclick - * @inheritdoc Ext.view.View#itemdblclick - */ - 'itemdblclick', - /** - * @event itemcontextmenu - * @inheritdoc Ext.view.View#itemcontextmenu - */ - 'itemcontextmenu', - /** - * @event beforecellclick - * @inheritdoc Ext.view.Table#beforecellclick - */ - 'beforecellclick', - /** - * @event cellclick - * @inheritdoc Ext.view.Table#cellclick - */ - 'cellclick', - /** - * @event beforecelldblclick - * @inheritdoc Ext.view.Table#beforecelldblclick - */ - 'beforecelldblclick', - /** - * @event celldblclick - * @inheritdoc Ext.view.Table#celldblclick - */ - 'celldblclick', - /** - * @event beforecellcontextmenu - * @inheritdoc Ext.view.Table#beforecellcontextmenu - */ - 'beforecellcontextmenu', - /** - * @event cellcontextmenu - * @inheritdoc Ext.view.Table#cellcontextmenu - */ - 'cellcontextmenu', - /** - * @event beforecellmousedown - * @inheritdoc Ext.view.Table#beforecellmousedown - */ - 'beforecellmousedown', - /** - * @event cellmousedown - * @inheritdoc Ext.view.Table#cellmousedown - */ - 'cellmousedown', - /** - * @event beforecellmouseup - * @inheritdoc Ext.view.Table#beforecellmouseup - */ - 'beforecellmouseup', - /** - * @event cellmouseup - * @inheritdoc Ext.view.Table#cellmouseup - */ - 'cellmouseup', - /** - * @event beforecellkeydown - * @inheritdoc Ext.view.Table#beforecellkeydown - */ - 'beforecellkeydown', - /** - * @event cellkeydown - * @inheritdoc Ext.view.Table#cellkeydown - */ - 'cellkeydown', - /** - * @event rowclick - * @inheritdoc Ext.view.Table#rowclick - */ - 'rowclick', - /** - * @event rowdblclick - * @inheritdoc Ext.view.Table#rowdblclick - */ - 'rowdblclick', - /** - * @event rowcontextmenu - * @inheritdoc Ext.view.Table#rowcontextmenu - */ - 'rowcontextmenu', - /** - * @event rowmousedown - * @inheritdoc Ext.view.Table#rowmousedown - */ - 'rowmousedown', - /** - * @event rowmouseup - * @inheritdoc Ext.view.Table#rowmouseup - */ - 'rowmouseup', - /** - * @event rowkeydown - * @inheritdoc Ext.view.Table#rowkeydown - */ - 'rowkeydown', - /** - * @event beforeitemkeydown - * @inheritdoc Ext.view.Table#beforeitemkeydown - */ - 'beforeitemkeydown', - /** - * @event itemkeydown - * @inheritdoc Ext.view.Table#itemkeydown - */ - 'itemkeydown', - /** - * @event beforeitemkeyup - * @inheritdoc Ext.view.Table#beforeitemkeyup - */ - 'beforeitemkeyup', - /** - * @event itemkeyup - * @inheritdoc Ext.view.Table#itemkeyup - */ - 'itemkeyup', - /** - * @event beforeitemkeypress - * @inheritdoc Ext.view.Table#beforeitemkeypress - */ - 'beforeitemkeypress', - /** - * @event itemkeypress - * @inheritdoc Ext.view.Table#itemkeypress - */ - 'itemkeypress', - /** - * @event beforecontainermousedown - * @inheritdoc Ext.view.View#beforecontainermousedown - */ - 'beforecontainermousedown', - /** - * @event beforecontainermouseup - * @inheritdoc Ext.view.View#beforecontainermouseup - */ - 'beforecontainermouseup', - /** - * @event beforecontainermouseover - * @inheritdoc Ext.view.View#beforecontainermouseover - */ - 'beforecontainermouseover', - /** - * @event beforecontainermouseout - * @inheritdoc Ext.view.View#beforecontainermouseout - */ - 'beforecontainermouseout', - /** - * @event beforecontainerclick - * @inheritdoc Ext.view.View#beforecontainerclick - */ - 'beforecontainerclick', - /** - * @event beforecontainerdblclick - * @inheritdoc Ext.view.View#beforecontainerdblclick - */ - 'beforecontainerdblclick', - /** - * @event beforecontainercontextmenu - * @inheritdoc Ext.view.View#beforecontainercontextmenu - */ - 'beforecontainercontextmenu', - /** - * @event beforecontainerkeydown - * @inheritdoc Ext.view.View#beforecontainerkeydown - */ - 'beforecontainerkeydown', - /** - * @event beforecontainerkeyup - * @inheritdoc Ext.view.View#beforecontainerkeyup - */ - 'beforecontainerkeyup', - /** - * @event beforecontainerkeypress - * @inheritdoc Ext.view.View#beforecontainerkeypress - */ - 'beforecontainerkeypress', - /** - * @event containermouseup - * @inheritdoc Ext.view.View#containermouseup - */ - 'containermouseup', - /** - * @event containermousedown - * @inheritdoc Ext.view.View#containermousedown - */ - 'containermousedown', - /** - * @event containermouseover - * @inheritdoc Ext.view.View#containermouseover - */ - 'containermouseover', - /** - * @event containermouseout - * @inheritdoc Ext.view.View#containermouseout - */ - 'containermouseout', - /** - * @event containerclick - * @inheritdoc Ext.view.View#containerclick - */ - 'containerclick', - /** - * @event containerdblclick - * @inheritdoc Ext.view.View#containerdblclick - */ - 'containerdblclick', - /** - * @event containercontextmenu - * @inheritdoc Ext.view.View#containercontextmenu - */ - 'containercontextmenu', - /** - * @event containerkeydown - * @inheritdoc Ext.view.View#containerkeydown - */ - 'containerkeydown', - /** - * @event containerkeyup - * @inheritdoc Ext.view.View#containerkeyup - */ - 'containerkeyup', - /** - * @event containerkeypress - * @inheritdoc Ext.view.View#containerkeypress - */ - 'containerkeypress', - /** - * @event selectionchange - * @inheritdoc Ext.selection.Model#selectionchange - */ - 'selectionchange', - /** - * @event beforeselect - * @inheritdoc Ext.selection.RowModel#beforeselect - */ - 'beforeselect', - /** - * @event select - * @inheritdoc Ext.selection.RowModel#select - */ - 'select', - /** - * @event beforedeselect - * @inheritdoc Ext.selection.RowModel#beforedeselect - */ - 'beforedeselect', - /** - * @event deselect - * @inheritdoc Ext.selection.RowModel#deselect - */ - 'deselect' - ]); - me.callParent(); - if (me.enableLocking) { - me.afterInjectLockable(); - } else { - delete headerCt.$initParent; - } - me.addStateEvents([ - 'columnresize', - 'columnmove', - 'columnhide', - 'columnshow', - 'sortchange', - 'filterchange', - 'groupchange' - ]); - }, - // rowBody feature events - /** - * @event beforerowbodymousedown - * @preventable - * @inheritdoc Ext.view.Table#event-beforerowbodymousedown - */ - /** - * @event beforerowbodymouseup - * @preventable - * @inheritdoc Ext.view.Table#event-beforerowbodymouseup - */ - /** - * @event beforerowbodyclick - * @preventable - * @inheritdoc Ext.view.Table#event-beforerowbodyclick - */ - /** - * @event beforerowbodydblclick - * @preventable - * @inheritdoc Ext.view.Table#event-beforerowbodydblclick - */ - /** - * @event beforerowbodycontextmenu - * @preventable - * @inheritdoc Ext.view.Table#event-beforerowbodycontextmenu - */ - /** - * @event beforerowbodylongpress - * @preventable - * @inheritdoc Ext.view.Table#event-beforerowbodylongpress - */ - /** - * @event beforerowbodykeydown - * @preventable - * @inheritdoc Ext.view.Table#event-beforerowbodykeydown - */ - /** - * @event beforerowbodykeyup - * @preventable - * @inheritdoc Ext.view.Table#event-beforerowbodykeyup - */ - /** - * @event beforerowbodykeypress - * @preventable - * @inheritdoc Ext.view.Table#event-beforerowbodykeypress - */ - /** - * @event rowbodymousedown - * @inheritdoc Ext.view.Table#event-rowbodymousedown - */ - /** - * @event rowbodymouseup - * @inheritdoc Ext.view.Table#event-rowbodymouseup - */ - /** - * @event rowbodyclick - * @inheritdoc Ext.view.Table#event-rowbodyclick - */ - /** - * @event rowbodydblclick - * @inheritdoc Ext.view.Table#event-rowbodydblclick - */ - /** - * @event rowbodycontextmenu - * @inheritdoc Ext.view.Table#event-rowbodycontextmenu - */ - /** - * @event rowbodylongpress - * @inheritdoc Ext.view.Table#event-rowbodylongpress - */ - /** - * @event rowbodykeydown - * @inheritdoc Ext.view.Table#event-rowbodykeydown - */ - /** - * @event rowbodykeyup - * @inheritdoc Ext.view.Table#event-rowbodykeyup - */ - /** - * @event rowbodykeypress - * @inheritdoc Ext.view.Table#event-rowbodykeypress - */ - beforeRender: function() { - var me = this, - bufferedRenderer = me.bufferedRenderer, - ariaAttr; - // If this is the topmost container of a lockable assembly, add the special class body - if (me.lockable) { - me.getProtoBody().addCls(me.lockingBodyCls); - } else // Don't create a buffered renderer for a locked grid. - { - // If we're auto heighting, we can't buffered render, so don't create it - if (bufferedRenderer && me.getSizeModel().height.auto) { - if (bufferedRenderer.isBufferedRenderer) { - Ext.raise('Cannot use buffered rendering with auto height'); - } - me.bufferedRenderer = bufferedRenderer = false; - } - if (bufferedRenderer && !bufferedRenderer.isBufferedRenderer) { - // Create a BufferedRenderer as a plugin if we have not already configured with one. - bufferedRenderer = { - xclass: 'Ext.grid.plugin.BufferedRenderer' - }; - Ext.copy(bufferedRenderer, me, 'variableRowHeight,numFromEdge,trailingBufferZone,leadingBufferZone,scrollToLoadBuffer'); - me.bufferedRenderer = me.addPlugin(bufferedRenderer); - } - ariaAttr = me.ariaRenderAttributes || (me.ariaRenderAttributes = {}); - ariaAttr['aria-readonly'] = !me.isEditable; - ariaAttr['aria-multiselectable'] = me.selModel.selectionMode !== 'SINGLE'; - } - me.callParent(arguments); - }, - onRender: function() { - var me = this, - gridPanelBorderWidth, totalColumnWidth; - // If this is the locked side, include border width in calculated locked grid width. - // TODO: Use shrinkWrapDock on the locked grid's headerCt when it works. - if (me.isLocked && me.getSizeModel().width.shrinkWrap) { - me.shrinkWrapColumns = true; - totalColumnWidth = me.headerCt.getTableWidth(); - if (isNaN(totalColumnWidth)) { - Ext.raise("Locked columns in an unsized locked side do NOT support a flex width."); - } - gridPanelBorderWidth = me.gridPanelBorderWidth || (me.gridPanelBorderWidth = me.el.getBorderWidth('lr')); - me.width = totalColumnWidth + gridPanelBorderWidth; - } - me.callParent(); - }, - /** - * Gets the {@link Ext.grid.header.Container headercontainer} for this grid / tree. - * @return {Ext.grid.header.Container} headercontainer - * - * **Note:** While a locked grid / tree will return an instance of - * {@link Ext.grid.locking.HeaderContainer} you will code to the - * {@link Ext.grid.header.Container} API. - */ - getHeaderContainer: function() { - return this.getView().getHeaderCt(); - }, - /** - * @inheritdoc Ext.grid.header.Container#getGridColumns - */ - getColumns: function() { - return this.getColumnManager().getColumns(); - }, - /** - * @inheritdoc Ext.grid.header.Container#getVisibleGridColumns - */ - getVisibleColumns: function() { - return this.getVisibleColumnManager().getColumns(); - }, - focus: function() { - // TablePanel is not focusable, but allow a call to delegate into the view - this.getView().focus(); - }, - /** - * Disables interaction with, and masks this grid's column headers. - */ - disableColumnHeaders: function() { - this.headerCt.disable(); - }, - /** - * Enables interaction with, and unmasks this grid's column headers after a call to {#disableColumnHeaders}. - */ - enableColumnHeaders: function() { - this.headerCt.enable(); - }, - /** - * @private - * Determine if there are any columns with a locked configuration option. - */ - hasLockedColumns: function(columns) { - var i, len, column; - // Fully instantiated HeaderContainer - if (columns.isRootHeader) { - columns = columns.items.items; - } - // Config object with items - else if (Ext.isObject(columns)) { - columns = columns.items; - } - for (i = 0 , len = columns.length; i < len; i++) { - column = columns[i]; - if (!column.processed && column.locked) { - return true; - } - } - }, - relayHeaderCtEvents: function(headerCt) { - this.relayEvents(headerCt, [ - /** - * @event columnresize - * @inheritdoc Ext.grid.header.Container#columnresize - */ - 'columnresize', - /** - * @event columnmove - * @inheritdoc Ext.grid.header.Container#columnmove - */ - 'columnmove', - /** - * @event columnhide - * @inheritdoc Ext.grid.header.Container#columnhide - */ - 'columnhide', - /** - * @event columnshow - * @inheritdoc Ext.grid.header.Container#columnshow - */ - 'columnshow', - /** - * @event columnschanged - * @inheritdoc Ext.grid.header.Container#columnschanged - */ - 'columnschanged', - /** - * @event sortchange - * @inheritdoc Ext.grid.header.Container#sortchange - */ - 'sortchange', - /** - * @event headerclick - * @inheritdoc Ext.grid.header.Container#headerclick - */ - 'headerclick', - /** - * @event headercontextmenu - * @inheritdoc Ext.grid.header.Container#headercontextmenu - */ - 'headercontextmenu', - /** - * @event headertriggerclick - * @inheritdoc Ext.grid.header.Container#headertriggerclick - */ - 'headertriggerclick' - ]); - }, - getState: function() { - var me = this, - state = me.callParent(), - storeState = me.store.getState(); - state = me.addPropertyToState(state, 'columns', me.headerCt.getColumnsState()); - if (storeState) { - state.storeState = storeState; - } - return state; - }, - applyState: function(state) { - var me = this, - sorter = state.sort, - storeState = state.storeState, - store = me.store, - columns = state.columns; - delete state.columns; - // Ensure superclass has applied *its* state. - // Component saves dimensions (and anchor/flex) plus collapsed state. - me.callParent(arguments); - if (columns) { - // Column state restoration needs to examine store state - me.headerCt.applyColumnsState(columns, storeState); - } - // Old stored sort state. Deprecated and will die out. - if (sorter) { - if (store.getRemoteSort()) { - // Pass false to prevent a sort from occurring. - store.sort({ - property: sorter.property, - direction: sorter.direction, - root: sorter.root - }, null, false); - } else { - store.sort(sorter.property, sorter.direction); - } - } - // New storeState which encapsulates groupers, sorters and filters. - else if (storeState) { - store.applyState(storeState); - } - }, - /** - * Returns the store associated with this Panel. - * @return {Ext.data.Store} The store - */ - getStore: function() { - return this.store; - }, - /** - * Gets the view for this panel. - * @return {Ext.view.Table} - */ - getView: function() { - var me = this, - scroll, scrollable, viewConfig; - if (!me.view) { - viewConfig = me.viewConfig; - scroll = viewConfig.scroll || me.scroll; - scrollable = me.scrollable; - if (scrollable == null && viewConfig.scrollable == null && scroll !== null) { - // transform deprecated scroll config into scrollable config - if (scroll === true || scroll === 'both') { - scrollable = true; - } else if (scroll === false || scroll === 'none') { - scrollable = false; - } else if (scroll === 'vertical') { - scrollable = { - x: false, - y: true - }; - } else if (scroll === 'horizontal') { - scrollable = { - x: true, - y: false - }; - } - } - viewConfig = Ext.apply({ - // TableView injects the view reference into this grid so that we have a reference as early as possible - // and Features need a reference to the grid. - // For these reasons, we configure a reference to this grid into the View - grid: me, - ownerGrid: me.ownerGrid, - deferInitialRefresh: me.deferRowRender, - variableRowHeight: me.variableRowHeight, - preserveScrollOnRefresh: true, - trackOver: me.trackMouseOver !== false, - throttledUpdate: me.throttledUpdate === true, - xtype: me.viewType, - store: me.store, - headerCt: me.headerCt, - columnLines: me.columnLines, - rowLines: me.rowLines, - navigationModel: 'grid', - features: me.features, - panel: me, - emptyText: me.emptyText || '' - }, me.viewConfig); - if (scrollable != null) { - viewConfig.scrollable = scrollable; - me.scrollable = null; - } - Ext.create(viewConfig); - // Normalize the application of the markup wrapping the emptyText config. - // `emptyText` can now be defined on the grid as well as on its viewConfig, and this led to the emptyText not - // having the wrapping markup when it was defined in the viewConfig. It should be backwards compatible. - // Note that in the unlikely event that emptyText is defined on both the grid config and the viewConfig that the viewConfig wins. - if (me.view.emptyText) { - me.view.emptyText = '
    ' + me.view.emptyText + '
    '; - } - // TableView's custom component layout, Ext.view.TableLayout requires a reference to the headerCt because it depends on the headerCt doing its work. - me.view.getComponentLayout().headerCt = me.headerCt; - me.mon(me.view, { - uievent: me.processEvent, - scope: me - }); - me.headerCt.view = me.view; - // Plugins and features may need to access the view as soon as it is created. - if (me.hasListeners.viewcreated) { - me.fireEvent('viewcreated', me, me.view); - } - } - return me.view; - }, - getColumnManager: function() { - return this.columnManager; - }, - getVisibleColumnManager: function() { - return this.visibleColumnManager; - }, - getTopLevelColumnManager: function() { - return this.ownerGrid.getColumnManager(); - }, - getTopLevelVisibleColumnManager: function() { - return this.ownerGrid.getVisibleColumnManager(); - }, - /** - * @private - * autoScroll is never valid for all classes which extend TablePanel. - */ - setAutoScroll: Ext.emptyFn, - applyScrollable: function(scrollable) { - if (this.view) { - this.view.setScrollable(scrollable); - } - return scrollable; - }, - getScrollable: function() { - return null; - }, - /** - * @private - * Processes UI events from the view. Propagates them to whatever internal Components need to process them. - * @param {String} type Event type, eg 'click' - * @param {Ext.view.Table} view TableView Component - * @param {HTMLElement} cell Cell HTMLElement the event took place within - * @param {Number} recordIndex Index of the associated Store Model (-1 if none) - * @param {Number} cellIndex Cell index within the row - * @param {Ext.event.Event} e Original event - */ - processEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) { - var header = e.position.column; - if (header) { - return header.processEvent.apply(header, arguments); - } - }, - /** - * Scrolls the specified record into view. - * @param {Number/String/Ext.data.Model} record The record, record id, or the zero-based position in the dataset to scroll to. - * @param {Object} [options] An object containing options to modify the operation. - * @param {Boolean} [options.animate] Pass `true` to animate the row into view. - * @param {Boolean} [options.highlight] Pass `true` to highlight the row with a glow animation when it is in view. - * @param {Boolean} [options.select] Pass as `true` to select the specified row. - * @param {Boolean} [options.focus] Pass as `true` to focus the specified row. - * @param {Function} [options.callback] A function to execute when the record is in view. This may be necessary if the - * first parameter is a record index and the view is backed by a {@link Ext.data.BufferedStore buffered store} - * which does not contain that record. - * @param {Boolean} options.callback.success `true` if acquiring the record's view node was successful. - * @param {Ext.data.Model} options.callback.record If successful, the target record. - * @param {HTMLElement} options.callback.node If successful, the record's view node. - * @param {Object} [options.scope] The scope (`this` reference) in which the callback function is executed. - */ - ensureVisible: function(record, options) { - this.doEnsureVisible(record, options); - }, - scrollByDeltaY: function(yDelta, animate) { - this.getView().scrollBy(0, yDelta, animate); - }, - scrollByDeltaX: function(xDelta, animate) { - this.getView().scrollBy(xDelta, 0, animate); - }, - afterCollapse: function() { - this.saveScrollPos(); - this.callParent(arguments); - }, - afterExpand: function() { - this.callParent(arguments); - this.restoreScrollPos(); - }, - saveScrollPos: Ext.emptyFn, - restoreScrollPos: Ext.emptyFn, - onHeaderResize: function() { - var scroller = this.view.getScrollable(), - size; - if (scroller && scroller.isTouchScroller) { - size = scroller.getSize(); - if (size) { - scroller.setSize({ - x: this.headerCt.getTableWidth(), - y: size.y - }); - } - } - }, - // Update the view when a header moves - onHeaderMove: function(headerCt, header, colsToMove, fromIdx, toIdx) { - var me = this; - // If there are Features or Plugins which create DOM which must match column order, they set the optimizedColumnMove flag to false. - // In this case we must refresh the view on column move. - if (me.optimizedColumnMove === false) { - me.view.refreshView(); - } else // Simplest case for default DOM structure is just to swap the columns round in the view. - { - me.view.moveColumn(fromIdx, toIdx, colsToMove); - } - me.delayScroll(); - }, - // Section onHeaderHide is invoked after view. - onHeaderHide: function(headerCt, header) { - var view = this.view; - // The headerCt may be hiding multiple children if a leaf level column - // causes a parent (and possibly other parents) to be hidden. Only run the refresh - // once we're done - if (!headerCt.childHideCount && view.refreshCounter) { - view.refreshView(); - } - }, - onHeaderShow: function(headerCt, header) { - var view = this.view; - if (view.refreshCounter) { - view.refreshView(); - } - }, - // To be triggered on add/remove/move for a leaf header - onHeadersChanged: function(headerCt, header) { - var me = this; - if (me.rendered && !me.reconfiguring) { - me.view.refreshView(); - me.delayScroll(); - } - }, - delayScroll: function() { - var target = this.view; - if (target) { - // Do not cause a layout by reading scrollX now. - // It must be read from the target when the task finally executes. - this.scrollTask.delay(10, null, null, [ - target - ]); - } - }, - /** - * @private - * Fires the TablePanel's viewready event when the view declares that its internal DOM is ready - */ - onViewReady: function() { - this.fireEvent('viewready', this); - }, - /** - * @private - * Tracks when things happen to the view and preserves the horizontal scroll position. - */ - onRestoreHorzScroll: function() { - var me = this, - x = me.scrollXPos; - if (x) { - // We need to restore the body scroll position here - me.syncHorizontalScroll(me, true); - } - }, - getScrollerOwner: function() { - var rootCmp = this; - if (!this.scrollerOwner) { - rootCmp = this.up('[scrollerOwner]'); - } - return rootCmp; - }, - /** - * Gets left hand side marker for header resizing. - * @private - */ - getLhsMarker: function() { - var me = this; - return me.lhsMarker || (me.lhsMarker = Ext.DomHelper.append(me.el, { - role: 'presentation', - cls: me.resizeMarkerCls - }, true)); - }, - /** - * Gets right hand side marker for header resizing. - * @private - */ - getRhsMarker: function() { - var me = this; - return me.rhsMarker || (me.rhsMarker = Ext.DomHelper.append(me.el, { - role: 'presentation', - cls: me.resizeMarkerCls - }, true)); - }, - /** - * Returns the grid's selection. See `{@link Ext.selection.Model#getSelection}`. - * @inheritdoc Ext.selection.Model#getSelection - */ - getSelection: function() { - return this.getSelectionModel().getSelection(); - }, - updateSelection: function(selection) { - var me = this, - sm; - if (!me.ignoreNextSelection) { - me.ignoreNextSelection = true; - sm = me.getSelectionModel(); - if (selection) { - sm.select(selection); - } else { - sm.deselectAll(); - } - me.ignoreNextSelection = false; - } - }, - updateBindSelection: function(selModel, selection) { - var me = this, - selected = null; - if (!me.ignoreNextSelection) { - me.ignoreNextSelection = true; - if (selection.length) { - selected = selModel.getLastSelected(); - me.hasHadSelection = true; - } - if (me.hasHadSelection) { - me.setSelection(selected); - } - me.ignoreNextSelection = false; - } - }, - updateHeaderBorders: function(headerBorders) { - this[headerBorders ? 'removeCls' : 'addCls'](this.noHeaderBordersCls); - }, - getNavigationModel: function() { - return this.getView().getNavigationModel(); - }, - /** - * Returns the selection model being used by this grid's {@link Ext.view.Table view}. - * @return {Ext.selection.Model} The selection model being used by this grid's {@link Ext.view.Table view}. - */ - getSelectionModel: function() { - return this.getView().getSelectionModel(); - }, - getScrollTarget: function() { - var items = this.getScrollerOwner().query('tableview'); - // Last view has the scroller - return items[items.length - 1]; - }, - syncHorizontalScroll: function(target, setBody) { - var me = this, - x = me.view.getScrollX(), - scrollTarget; - setBody = setBody === true; - // Only set the horizontal scroll if we've changed position, - // so that we don't set this on vertical scrolls - if (me.rendered && (setBody || x !== me.scrollXPos)) { - // Only set the body position if we're reacting to a refresh, otherwise - // we just need to set the header. - if (setBody) { - scrollTarget = me.getScrollTarget(); - scrollTarget.setScrollX(x); - } - me.headerCt.setScrollX(x); - me.scrollXPos = x; - } - }, - // template method meant to be overriden - onStoreLoad: Ext.emptyFn, - getEditorParent: function() { - return this.body; - }, - bindStore: function(store, initial) { - var me = this, - view = me.getView(); - // Normally, this method will always be called with a valid store (because there is a symmetric - // .unbindStore method), but there are cases where this method will be called and passed a null - // value, i.e., a panel is used as a pickerfield. See EXTJS-13089. - if (store) { - // Bind to store immediately because subsequent processing looks for grid's store property - me.store = store; - if (view.store !== store) { - // If coming from a reconfigure, we need to set the actual store property on the view. Setting the - // store will then also set the dataSource. - // - // Note that if it's a grid feature then this is sorted out in view.bindStore(), and it's own - // implementation of .bindStore() will be called. - view.bindStore(store, false); - } - me.mon(store, { - load: me.onStoreLoad, - scope: me - }); - me.storeRelayers = me.relayEvents(store, [ - /** - * @event filterchange - * @inheritdoc Ext.data.Store#filterchange - */ - 'filterchange', - /** - * @event groupchange - * @inheritdoc Ext.data.Store#groupchange - */ - 'groupchange' - ]); - } else { - me.unbindStore(); - } - }, - unbindStore: function() { - var me = this, - store = me.store, - view; - if (store) { - store.trackStateChanges = false; - me.store = null; - me.mun(store, { - load: me.onStoreLoad, - scope: me - }); - Ext.destroy(me.storeRelayers); - view = me.view; - if (view.store) { - view.bindStore(null); - } - } - }, - setColumns: function(columns) { - // If being reconfigured from zero columns to zero columns, skip operation. - // This can happen if columns are being set from a binding and the initial value - // of the bound data in the ViewModel is [] - if (columns.length || this.getColumnManager().getColumns().length) { - this.reconfigure(undefined, columns); - } - }, - /** - * A convenience method that fires {@link #reconfigure} with the store param. To set the store AND change columns, - * use the {@link #reconfigure reconfigure method}. - * - * @param {Ext.data.Store} [store] The new store. - */ - setStore: function(store) { - this.reconfigure(store); - if (this.autoLoad && !store.isEmptyStore && !(store.loading || store.isLoaded())) { - store.load(); - } - }, - /** - * Reconfigures the grid or tree with a new store and/or columns. Stores and columns - * may also be passed as params. - * - * grid.reconfigure(store, columns); - * - * Additionally, you can pass just a store or columns. - * - * tree.reconfigure(store); - * // or - * grid.reconfigure(columns); - * // or - * tree.reconfigure(null, columns); - * - * If you're using locked columns, the {@link #enableLocking} config should be set - * to `true` before the reconfigure method is executed. - * - * @param {Ext.data.Store/Object} [store] The new store instance or store config. You can - * pass `null` if no new store. - * @param {Object[]} [columns] An array of column configs - */ - reconfigure: function(store, columns) { - var me = this, - oldStore = me.store, - headerCt = me.headerCt, - lockable = me.lockable, - oldColumns = headerCt ? headerCt.items.getRange() : me.columns, - view = me.getView(), - block, refreshCounter; - // Allow optional store argument to be fully omitted, and the columns argument to be solo - if (arguments.length === 1 && Ext.isArray(store)) { - columns = store; - store = null; - } - // Make copy in case the beforereconfigure listener mutates it. - if (columns) { - columns = Ext.Array.slice(columns); - } - me.reconfiguring = true; - if (store) { - store = Ext.StoreManager.lookup(store); - } - me.fireEvent('beforereconfigure', me, store, columns, oldStore, oldColumns); - Ext.suspendLayouts(); - if (lockable) { - me.reconfigureLockable(store, columns); - } else { - // Prevent the view from refreshing until we have resumed layouts and any columns are rendered - block = view.blockRefresh; - view.blockRefresh = true; - // The following test compares the result of an assignment of the store var with the oldStore var. - // This saves a large amount of code. - // - // Note that we need to process the store first in case one or more passed columns (if there are any) - // have active gridfilters with values which would filter the currently-bound store. - if (store && store !== oldStore) { - me.unbindStore(); - me.bindStore(store); - } - if (columns) { - // new columns, delete scroll pos - delete me.scrollXPos; - headerCt.removeAll(); - headerCt.add(columns); - } - view.blockRefresh = block; - refreshCounter = view.refreshCounter; - } - Ext.resumeLayouts(true); - if (lockable) { - me.afterReconfigureLockable(); - } else if (view.refreshCounter === refreshCounter) { - // If the layout resumption didn't trigger the view to refresh, do it here - view.refreshView(); - } - me.fireEvent('reconfigure', me, store, columns, oldStore, oldColumns); - delete me.reconfiguring; - }, - beforeDestroy: function() { - var me = this, - task = me.scrollTask; - if (task) { - task.cancel(); - me.scrollTask = null; - } - Ext.destroy(me.focusEnterLeaveListeners); - me.callParent(); - }, - onDestroy: function() { - var me = this; - if (me.lockable) { - me.destroyLockable(); - } - me.unbindStore(); - me.callParent(); - me.columns = me.storeRelayers = me.columnManager = me.visibleColumnManager = null; - }, - destroy: function() { - // Clear out references here because other things (plugins/features) may need to know about them during destruction - var me = this; - me.callParent(); - if (me.destroyed) { - me.view = me.selModel = me.headerCt = null; - } - }, - privates: { - // The focusable flag is set, but there is no focusable element. - // Focus is delegated to the view by the focus implementation. - initFocusableElement: function() {}, - doEnsureVisible: function(record, options) { - // Handle the case where this is a lockable assembly - if (this.lockable) { - return this.ensureLockedVisible(record, options); - } - // Allow them to pass the record id. - if (typeof record !== 'number' && !record.isEntity) { - record = this.store.getById(record); - } - var me = this, - view = me.getView(), - domNode = view.getNode(record), - callback, scope, animate, highlight, select, doFocus, scrollable, column, cell; - if (options) { - callback = options.callback; - scope = options.scope; - animate = options.animate; - highlight = options.highlight; - select = options.select; - doFocus = options.focus; - column = options.column; - } - // Always supercede any prior deferred request - if (me.deferredEnsureVisible) { - me.deferredEnsureVisible.destroy(); - } - // We have not yet run the layout. - // Add this to the end of the first sizing process. - // By using the resize event, we will come in AFTER any Component's onResize and onBoxReady handling. - if (!view.componentLayoutCounter) { - me.deferredEnsureVisible = view.on({ - resize: me.doEnsureVisible, - args: Ext.Array.slice(arguments), - scope: me, - single: true, - destroyable: true - }); - return; - } - if (typeof column === 'number') { - column = me.ownerGrid.getVisibleColumnManager().getColumns()[column]; - } - // We found the DOM node associated with the record - if (domNode) { - scrollable = view.getScrollable(); - if (column) { - cell = Ext.fly(domNode).selectNode(column.getCellSelector()); - } - if (scrollable) { - scrollable.scrollIntoView(cell || domNode, !!column, animate, highlight); - } - if (!record.isEntity) { - record = view.getRecord(domNode); - } - if (select) { - view.getSelectionModel().select(record); - } - if (doFocus) { - view.getNavigationModel().setPosition(record, 0); - } - Ext.callback(callback, scope || me, [ - true, - record, - domNode - ]); - } - // If we didn't find it, it's probably because of buffered rendering - else if (view.bufferedRenderer) { - view.bufferedRenderer.scrollTo(record, { - animate: animate, - highlight: highlight, - select: select, - focus: doFocus, - column: column, - callback: function(recordIdx, record, domNode) { - Ext.callback(callback, scope || me, [ - true, - record, - domNode - ]); - } - }); - } else { - Ext.callback(callback, scope || me, [ - false, - null - ]); - } - }, - getFocusEl: function() { - return this.getView().getFocusEl(); - }, - /** - * Toggles ARIA actionable mode on/off - * @param {Boolean} enabled - * @return {Boolean} `true` if actionable mode was entered - * @private - */ - setActionableMode: function(enabled, position) { - // Always set the topmost grid in a lockable assembly - var me = this.ownerGrid; - // Can be called to exit actionable mode upon a focusLeave caused by destruction - if (!me.destroying && me.view.setActionableMode(enabled, position) !== false) { - me.fireEvent('actionablemodechange', enabled); - me[enabled ? 'addCls' : 'removeCls'](me.actionableModeCls); - return true; - } - } - } -}); - -/** - * @private - * - * This class is used only by the grid's HeaderContainer docked child. - * - * It adds the ability to shrink the vertical size of the inner container element back if a grouped - * column header has all its child columns dragged out, and the whole HeaderContainer needs to shrink back down. - * - * Also, after every layout, after all headers have attained their 'stretchmax' height, it goes through and calls - * `setPadding` on the columns so that they lay out correctly. - */ -Ext.define('Ext.grid.ColumnLayout', { - extend: 'Ext.layout.container.HBox', - alias: 'layout.gridcolumn', - type: 'gridcolumn', - requires: [ - 'Ext.panel.Table' - ], - firstHeaderCls: Ext.baseCSSPrefix + 'column-header-first', - lastHeaderCls: Ext.baseCSSPrefix + 'column-header-last', - initLayout: function() { - this.callParent(); - if (this.scrollbarWidth === undefined) { - this.self.prototype.scrollbarWidth = Ext.getScrollbarSize().width; - } - }, - beginLayout: function(ownerContext) { - var me = this, - owner = me.owner, - firstCls = me.firstHeaderCls, - lastCls = me.lastHeaderCls, - bothCls = [ - firstCls, - lastCls - ], - items = me.getVisibleItems(), - len = items.length, - i, item; - me.callParent([ - ownerContext - ]); - // Sync the first/lastCls states for all the headers. - for (i = 0; i < len; i++) { - item = items[i]; - if (len === 1) { - // item is the only item so it is both first and last - item.addCls(bothCls); - } else if (i === 0) { - // item is the first of 2+ items - item.addCls(firstCls); - item.removeCls(lastCls); - } else if (i === len - 1) { - // item is the last of 2+ items - item.removeCls(firstCls); - item.addCls(lastCls); - } else { - item.removeCls(bothCls); - } - } - // Start this at 0 and for the root headerCt call determineScrollbarWidth to get - // it set properly. Typically that amounts to a "delete" to expose the system's - // scrollbar width stored on our prototype. - me.scrollbarWidth = 0; - if (owner.isRootHeader) { - me.determineScrollbarWidth(ownerContext); - } - if (!me.scrollbarWidth) { - // By default Mac OS X has overlay scrollbars that do not take space, but also - // the RTL override may have set this to 0... so make sure we don't try to - // compensate for a scrollbar when there isn't one. - ownerContext.manageScrollbar = false; - } - }, - moveItemBefore: function(item, before) { - var prevOwner = item.ownerCt; - // Due to the nature of grid headers, index calculation for - // moving items is complicated, especially since removals can trigger - // groups to be removed (and thus alter indexes). As such, the logic - // is simplified by removing the item first, then calculating the index - // and inserting it - if (item !== before && prevOwner) { - prevOwner.remove(item, false); - } - return this.callParent([ - item, - before - ]); - }, - determineScrollbarWidth: function(ownerContext) { - var me = this, - owner = me.owner, - grid = owner.grid, - // locking headerCt can refuse to reserveScrollbar, even if the locking grid - // view does reserveScrollbar (special technique for hiding the vertical - // scrollbar on the locked side) - vetoReserveScrollbar = owner.reserveScrollbar === false, - // We read this value off of the immediate grid since the locked side of a - // locking grid will not have this set. The ownerGrid in that case would have - // it set but will pass along true only to the normal side. - reserveScrollbar = grid.reserveScrollbar && !vetoReserveScrollbar, - manageScrollbar = !reserveScrollbar && !vetoReserveScrollbar && grid.view.scrollFlags.y; - // If we have reserveScrollbar then we will always have a vertical scrollbar so - // manageScrollbar should be false. Otherwise it is based on overflow-y: - ownerContext.manageScrollbar = manageScrollbar; - // Determine if there is any need to deal with the width of the vertical scrollbar - // and set "scrollbarWidth" to 0 if not or the system determined value (stored on - // our prototype). - // - if (!grid.ownerGrid.collapsed && (reserveScrollbar || manageScrollbar)) { - // Ensure the real scrollbarWidth value is exposed from the prototype. This - // may be needed if the scrollFlags have changed since we may have a 0 set on - // this instance from a previous layout run. - delete me.scrollbarWidth; - } - }, - // On return, the RTL override (Ext.rtl.grid.ColumnLayout) will deal with various - // browser bugs and may set me.scrollbarWidth to 0 or a negative value. - calculate: function(ownerContext) { - var me = this, - grid = me.owner.grid, - // Our TableLayout buddy sets this in its beginLayout so we can work this - // out together: - viewContext = ownerContext.viewContext, - state = ownerContext.state, - context = ownerContext.context, - lockingPartnerContext, ownerGrid, columnsChanged, columns, len, i, column, scrollbarAdjustment, viewOverflowY; - me.callParent([ - ownerContext - ]); - if (grid && state.parallelDone) { - lockingPartnerContext = viewContext.lockingPartnerContext; - ownerGrid = grid.ownerGrid; - // A force-fit needs to be "reflexed" so check that now. If we have to reflex - // the items, we need to re-cacheFlexes and invalidate ourselves. - if (ownerGrid.forceFit && !state.reflexed) { - if (me.convertWidthsToFlexes(ownerContext)) { - me.cacheFlexes(ownerContext); - me.done = false; - ownerContext.invalidate({ - state: { - reflexed: true, - scrollbarAdjustment: me.getScrollbarAdjustment(ownerContext) - } - }); - return; - } - } - // Once the parallelDone flag goes up, we need to pack up the changed column - // widths for our TableLayout partner. - if ((columnsChanged = state.columnsChanged) === undefined) { - columns = ownerContext.target.getVisibleGridColumns(); - columnsChanged = false; - for (i = 0 , len = columns.length; i < len; i++) { - column = context.getCmp(columns[i]); - // Since we are parallelDone, all of the children should have width, - // so we can - if (!column.lastBox || column.props.width !== column.lastBox.width) { - (columnsChanged || (columnsChanged = []))[i] = column; - } - } - state.columnsChanged = columnsChanged; - // This will trigger our TableLayout partner and allow it to proceed. - ownerContext.setProp('columnsChanged', columnsChanged); - } - if (ownerContext.manageScrollbar) { - // If we changed the column widths, we need to wait for the TableLayout to - // return whether or not we have overflowY... well, that is, if we are - // needing to tweak the scrollbarAdjustment... - scrollbarAdjustment = me.getScrollbarAdjustment(ownerContext); - if (scrollbarAdjustment) { - // Since we start with the assumption that we will need the scrollbar, - // we now need to wait to see if our guess was correct. - viewOverflowY = viewContext.getProp('viewOverflowY'); - if (viewOverflowY === undefined) { - // The TableLayout has not determined this yet, so park it. - me.done = false; - return; - } - if (!viewOverflowY) { - // We have our answer, and it turns out the view did not overflow - // (even with the reduced width we gave it), so we need to remove - // the scrollbarAdjustment and go again. - if (lockingPartnerContext) { - // In a locking grid, only the normal side plays this game, - // so now that we know the resolution, we need to invalidate - // the locking view and its headerCt. - lockingPartnerContext.invalidate(); - lockingPartnerContext.headerContext.invalidate(); - } - viewContext.invalidate(); - ownerContext.invalidate({ - state: { - // Pass a 0 adjustment on into our next life. If this is - // the invalidate that resets ownerContext then this is - // put onto the new state. If not, it will reset back to - // undefined and we'll have to begin again (which is the - // correct thing to do in that case). - scrollbarAdjustment: 0 - } - }); - } - } - } - } - }, - // else { - // We originally assumed we would need the scrollbar and since we do - // not now, we must be on the second pass, so we can move on... - // } - finishedLayout: function(ownerContext) { - this.callParent([ - ownerContext - ]); - if (this.owner.ariaRole === 'rowgroup') { - this.innerCt.dom.setAttribute('role', 'row'); - } - // Wipe this array because it holds component references and gets cached on the object - // Can cause a circular reference - ownerContext.props.columnsChanged = null; - }, - convertWidthsToFlexes: function(ownerContext) { - var me = this, - totalWidth = 0, - calculated = me.sizeModels.calculated, - childItems, len, i, childContext, item; - childItems = ownerContext.childItems; - len = childItems.length; - for (i = 0; i < len; i++) { - childContext = childItems[i]; - item = childContext.target; - totalWidth += childContext.props.width; - // Only allow to be flexed if it's a resizable column - if (!(item.fixed || item.resizable === false)) { - // For forceFit, just use allocated width as the flex value, and the proportions - // will end up the same whatever HeaderContainer width they are being forced into. - item.flex = ownerContext.childItems[i].flex = childContext.props.width; - item.width = null; - childContext.widthModel = calculated; - } - } - // Only need to loop back if the total column width is not already an exact fit - return totalWidth !== ownerContext.props.width; - }, - getScrollbarAdjustment: function(ownerContext) { - var me = this, - state = ownerContext.state, - grid = me.owner.grid, - scrollbarAdjustment = state.scrollbarAdjustment; - // If there is potential for a vertical scrollbar, then we start by assuming - // we will need to reserve space for it. Unless, of course, there are no - // records! - if (scrollbarAdjustment === undefined) { - scrollbarAdjustment = 0; - if (grid.reserveScrollbar || (ownerContext.manageScrollbar && !grid.ownerGrid.layout.ownerContext.heightModel.shrinkWrap)) { - scrollbarAdjustment = me.scrollbarWidth; - } - state.scrollbarAdjustment = scrollbarAdjustment; - } - return scrollbarAdjustment; - }, - /** - * @private - * Local getContainerSize implementation accounts for vertical scrollbar in the view. - */ - getContainerSize: function(ownerContext) { - var me = this, - got, needed, padding, gotWidth, gotHeight, width, height, result; - if (me.owner.isRootHeader) { - result = me.callParent([ - ownerContext - ]); - if (result.gotWidth) { - result.width -= me.getScrollbarAdjustment(ownerContext); - } - } else { - padding = ownerContext.paddingContext.getPaddingInfo(); - got = needed = 0; - // The container size here has to be provided by the ColumnComponentLayout to - // account for borders in its odd way. - if (!ownerContext.widthModel.shrinkWrap) { - ++needed; - width = ownerContext.getProp('innerWidth'); - gotWidth = (typeof width === 'number'); - if (gotWidth) { - ++got; - width -= padding.width; - if (width < 0) { - width = 0; - } - } - } - if (!ownerContext.heightModel.shrinkWrap) { - ++needed; - height = ownerContext.getProp('innerHeight'); - gotHeight = (typeof height === 'number'); - if (gotHeight) { - ++got; - height -= padding.height; - if (height < 0) { - height = 0; - } - } - } - return { - width: width, - height: height, - needed: needed, - got: got, - gotAll: got === needed, - gotWidth: gotWidth, - gotHeight: gotHeight - }; - } - return result; - }, - publishInnerCtSize: function(ownerContext) { - var me = this, - owner = me.owner, - cw = ownerContext.peek('contentWidth'), - adjustment = 0; - // Pass negative "reservedSpace", so that the innerCt gets *extra* size to accommodate the view's vertical scrollbar - if (cw != null && owner.isRootHeader) { - adjustment = -ownerContext.state.scrollbarAdjustment; - } - return me.callParent([ - ownerContext, - adjustment - ]); - } -}); - -/** - * @private - * - * Manages and provides information about a TablePanel's *visible leaf* columns. - */ -Ext.define('Ext.grid.ColumnManager', { - alternateClassName: [ - 'Ext.grid.ColumnModel' - ], - columns: null, - constructor: function(visibleOnly, headerCt, secondHeaderCt) { - if (!headerCt.isRootHeader && !headerCt.isGroupHeader) { - Ext.raise('ColumnManager must be passed an instantiated HeaderContainer or group header'); - } - this.headerCt = headerCt; - // We are managing columns for a lockable grid... - if (secondHeaderCt) { - if (!headerCt.isRootHeader && !headerCt.isGroupHeader) { - Ext.raise('ColumnManager must be passed an instantiated HeaderContainer or group header'); - } - this.secondHeaderCt = secondHeaderCt; - } - this.visibleOnly = !!visibleOnly; - }, - getColumns: function() { - if (!this.columns) { - this.cacheColumns(); - } - return this.columns; - }, - hasVariableRowHeight: function() { - var me = this, - columns = me.getColumns(), - len = columns.length, - i; - if (me.variableRowHeight == null) { - me.variableRowHeight = false; - for (i = 0; !me.variableRowHeight && i < len; i++) { - me.variableRowHeight = !!columns[i].variableRowHeight; - } - } - return me.variableRowHeight; - }, - /** - * If called from a root header, returns the index of a leaf level header regardless of what the nesting - * structure is. - * - * If called from a group header, returns the index of a leaf level header relative to the group header. - * - * If a group header is passed, the index of the first leaf level header within it is returned. - * - * @param {Ext.grid.column.Column} header The header to find the index of - * @return {Number} The index of the specified column header - */ - getHeaderIndex: function(header) { - if (header.isGroupHeader) { - // Get the first header for the particular group header. The .getHeaderColumns API - // will sort out if it's to be just visible columns or all columns. - header = this.getHeaderColumns(header)[0]; - } - return Ext.Array.indexOf(this.getColumns(), header); - }, - /** - * If called from a root header, gets a leaf level header by index regardless of what the nesting - * structure is. - * - * If called from a group header, returns the index of a leaf level header relative to the group header. - * - * @param {Number} index The column index for which to retrieve the column. - * @return {Ext.grid.column.Column} The header. `null` if it doesn't exist. - */ - getHeaderAtIndex: function(index) { - var columns = this.getColumns(), - col = columns[index]; - return col || null; - }, - getPreviousSibling: function(header) { - var index = this.getHeaderIndex(header), - col = null; - if (index > 0) { - col = this.getColumns()[index - 1]; - } - return col; - }, - getNextSibling: function(header) { - var index = this.getHeaderIndex(header), - col; - if (index !== -1) { - col = this.getColumns()[index + 1]; - } - return col || null; - }, - /** - * Get the first column. - * @return {Ext.grid.column.Column} The header. `null` if it doesn't exist - */ - getFirst: function() { - var columns = this.getColumns(); - return columns.length > 0 ? columns[0] : null; - }, - /** - * Get the last column. - * @return {Ext.grid.column.Column} The header. `null` if it doesn't exist - */ - getLast: function() { - var columns = this.getColumns(), - len = columns.length; - return len > 0 ? columns[len - 1] : null; - }, - /** - * Get a leaf level header by data index regardless of what the nesting - * structure is. - * @param {String} dataIndex The data index - * @return {Ext.grid.column.Column} The header. `null` if it doesn't exist. - */ - getHeaderByDataIndex: function(dataIndex) { - var columns = this.getColumns(), - len = columns.length, - i, header; - for (i = 0; i < len; ++i) { - header = columns[i]; - if (header.dataIndex === dataIndex) { - return header; - } - } - return null; - }, - /** - * Get a leaf level header by index regardless of what the nesting - * structure is. - * @param {String} id The id - * @return {Ext.grid.column.Column} The header. `null` if it doesn't exist. - */ - getHeaderById: function(id) { - var columns = this.getColumns(), - len = columns.length, - i, header; - for (i = 0; i < len; ++i) { - header = columns[i]; - if (header.getItemId() === id) { - return header; - } - } - return null; - }, - /** - * When passed a column index, returns the closet *visible* column to that. If the column at the passed index is visible, - * that is returned. If it is hidden, either the next visible, or the previous visible column is returned. - * - * If called from a group header, returns the visible index of a leaf level header relative to the group header with the - * same stipulations as outlined above. - * - * @param {Number} index Position at which to find the closest visible column. - */ - getVisibleHeaderClosestToIndex: function(index) { - var result = this.getHeaderAtIndex(index); - if (result && result.hidden) { - result = result.next(':not([hidden])') || result.prev(':not([hidden])'); - } - return result; - }, - cacheColumns: function() { - var columns = this.getHeaderColumns(this.headerCt), - second = this.secondHeaderCt; - if (second) { - columns = columns.concat(this.getHeaderColumns(second)); - } - this.columns = columns; - }, - getHeaderColumns: function(header) { - var result = this.visibleOnly ? header.getVisibleGridColumns() : header.getGridColumns(); - return Ext.Array.clone(result); - }, - invalidate: function() { - var root = this.rootColumns; - this.columns = this.variableRowHeight = null; - // If we are part of a lockable assembly, invalidate the root column manager - if (root) { - root.invalidate(); - } - }, - destroy: function() { - this.columns = this.rootColumns = null; - this.callParent(); - } -}, function() { - this.createAlias('indexOf', 'getHeaderIndex'); -}); - -// TODO: Implement http://www.w3.org/TR/2013/WD-wai-aria-practices-20130307/#grid standards -/** - * @class Ext.grid.NavigationModel - * @private - * This class listens for key events fired from a {@link Ext.grid.Panel GridPanel}, and moves the currently focused item - * by adding the class {@link #focusCls}. - */ -Ext.define('Ext.grid.NavigationModel', { - extend: 'Ext.view.NavigationModel', - alias: 'view.navigation.grid', - /** - * @event navigate Fired when a key has been used to navigate around the view. - * @param {Object} event - * @param {Ext.event.Event} event.keyEvent The key event which caused the navigation. - * @param {Number} event.previousRecordIndex The previously focused record index. - * @param {Ext.data.Model} event.previousRecord The previously focused record. - * @param {HTMLElement} event.previousItem The previously focused grid cell. - * @param {Ext.grid.Column} event.previousColumn The previously focused grid column. - * @param {Number} event.recordIndex The newly focused record index. - * @param {Ext.data.Model} event.record the newly focused record. - * @param {HTMLElement} event.item the newly focused grid cell. - * @param {Ext.grid.Column} event.column The newly focused grid column. - */ - focusCls: Ext.baseCSSPrefix + 'grid-item-focused', - getViewListeners: function() { - var me = this; - return { - focusmove: { - element: 'el', - fn: me.onFocusMove - }, - containermousedown: me.onContainerMouseDown, - cellmousedown: me.onCellMouseDown, - // We focus on click if the mousedown handler did not focus because it was a translated "touchstart" event. - cellclick: me.onCellClick, - itemmousedown: me.onItemMouseDown, - // We focus on click if the mousedown handler did not focus because it was a translated "touchstart" event. - itemclick: me.onItemClick, - itemcontextmenu: me.onItemClick, - scope: me - }; - }, - initKeyNav: function(view) { - var me = this; - // We will have two keyNavs if we are the navigation model for a lockable assembly - if (!me.keyNav) { - me.keyNav = []; - me.position = new Ext.grid.CellContext(view); - } - // Drive the KeyNav off the View's itemkeydown event so that beforeitemkeydown listeners may veto. - // By default KeyNav uses defaultEventAction: 'stopEvent', and this is required for movement keys - // which by default affect scrolling. - me.keyNav.push(new Ext.util.KeyNav({ - target: view, - ignoreInputFields: true, - eventName: 'itemkeydown', - defaultEventAction: 'stopEvent', - processEvent: me.processViewEvent, - up: me.onKeyUp, - down: me.onKeyDown, - right: me.onKeyRight, - left: me.onKeyLeft, - pageDown: me.onKeyPageDown, - pageUp: me.onKeyPageUp, - home: me.onKeyHome, - end: me.onKeyEnd, - space: me.onKeySpace, - enter: me.onKeyEnter, - esc: me.onKeyEsc, - 113: me.onKeyF2, - tab: me.onKeyTab, - A: { - ctrl: true, - // Need a separate function because we don't want the key - // events passed on to selectAll (causes event suppression). - handler: me.onSelectAllKeyPress - }, - scope: me - })); - }, - addKeyBindings: function(binding) { - var len = this.keyNav.length, - i; - // We will have two keyNavs if we are the navigation model for a lockable assembly - for (i = 0; i < len; i++) { - this.keyNav[i].addBindings(binding); - } - }, - enable: function() { - var len = this.keyNav.length, - i; - // We will have two keyNavs if we are the navigation model for a lockable assembly - for (i = 0; i < len; i++) { - this.keyNav[i].enable(); - } - this.disabled = false; - }, - disable: function() { - var len = this.keyNav.length, - i; - // We will have two keyNavs if we are the navigation model for a lockable assembly - for (i = 0; i < len; i++) { - this.keyNav[i].disable(); - } - this.disabled = true; - }, - /** - * @private - * Every key event is tagged with the source view, so the NavigationModel is independent. - * Called in the scope of the KeyNav. This function is injected into the NavigationModel's - * {@link Ext.util.KeyNav KeyNav) as its {@link Ext.util.KeyNav#processEvent processEvent} config. - */ - processViewEvent: function(view, record, row, recordIndex, event) { - var key = event.getKey(); - // In actionable mode, we only listen for TAB, F2 and ESC to exit actionable mode - if (view.actionableMode) { - this.map.ignoreInputFields = false; - if (key === event.TAB || key === event.ESC || key === event.F2) { - return event; - } - } else // In navigation mode, we process all keys - { - this.map.ignoreInputFields = true; - // Ignore TAB key in navigable mode - return key === event.TAB ? null : event; - } - }, - onCellMouseDown: function(view, cell, cellIndex, record, row, recordIndex, mousedownEvent) { - var targetComponent = Ext.Component.fromElement(mousedownEvent.target, cell), - ac; - // If actionable mode, and - // (mousedown on a tabbable, or anywhere in the ownership tree of the active component), - // we should not get involved. - // The tabbable element will be part of actionability. - if (view.actionableMode && (mousedownEvent.getTarget(null, null, true).isTabbable() || ((ac = Ext.ComponentManager.getActiveComponent()) && ac.owns(mousedownEvent)))) { - return; - } - // If the event is a touchstart, leave it until the click to focus. - if (mousedownEvent.pointerType !== 'touch') { - this.setPosition(mousedownEvent.position, null, mousedownEvent); - } - // If mousedowning on a focusable Component. - // After having set the position according to the mousedown, we then - // enter actionable mode and focus the component just as if the user - // Had navigated here and pressed F2. - if (targetComponent && targetComponent.isFocusable && targetComponent.isFocusable()) { - view.setActionableMode(true, mousedownEvent.position); - // Focus the targeted Component - targetComponent.focus(); - } - }, - onCellClick: function(view, cell, cellIndex, record, row, recordIndex, clickEvent) { - var me = this, - targetComponent = Ext.Component.fromElement(clickEvent.target, cell), - clickOnFocusable = targetComponent && targetComponent.isFocusable && targetComponent.isFocusable(); - // In actionable mode, we fire a navigate event in case the column's stopSelection is false - if (view.actionableMode) { - // Only continue if action position is in a different place - // Test using the guaranteed present clickEvent.position. - // actionPosition might be null if action is right now in the other - // side of a lockable. - if (!clickEvent.position.isEqual(view.actionPosition)) { - // Must still set position so that the other actionable - // at the different action position blurs and finishes. - if (!clickOnFocusable) { - me.setPosition(clickEvent.position, null, clickEvent); - } - } - me.fireEvent('navigate', { - view: view, - navigationModel: me, - keyEvent: clickEvent, - previousPosition: me.previousPosition, - previousRecordIndex: me.previousRecordIndex, - previousRecord: me.previousRecord, - previousItem: me.previousItem, - previousCell: me.previousCell, - previousColumnIndex: me.previousColumnIndex, - previousColumn: me.previousColumn, - position: clickEvent.position, - recordIndex: clickEvent.position.rowIdx, - record: clickEvent.position.record, - item: clickEvent.item, - cell: clickEvent.position.cellElement, - columnIndex: clickEvent.position.colIdx, - column: clickEvent.position.column - }); - } else { - // If the mousedown that initiated the click has navigated us to the correct spot, just fire the event - if (me.position.isEqual(clickEvent.position) || clickOnFocusable) { - me.fireNavigateEvent(clickEvent); - } else { - me.setPosition(clickEvent.position, null, clickEvent); - } - } - }, - /** - * @private - * @param {Ext.event.Event} e The focusmove event - * This is where we are informed of intra-view cell navigation which may be caused by screen readers. - * We have to react to that and keep our internal state consistent. - */ - onFocusMove: function(e) { - var cell = e.target, - view = Ext.Component.fromElement(e.delegatedTarget, null, 'tableview'), - cell = e.target, - record, column, newPosition; - // If what was focused was a cell, we can set our position to that cell. - if (view && Ext.fly(cell).is(view.cellSelector)) { - // Ignore if the cause of focus move is actionable mode tabbing - if (view.actionableModeTabbing) { - return; - } - // If we focus a cell, we must exit actionable mode - view.ownerGrid.setActionableMode(false); - record = view.getRecord(cell); - column = view.getHeaderByCell(cell); - if (record && column) { - newPosition = new Ext.grid.CellContext(view).setPosition(record, column); - // The focus might have been the *result* of setting the position - if (!newPosition.isEqual(this.position)) { - this.setPosition(newPosition); - } - } - } - }, - onItemMouseDown: function(view, record, item, index, mousedownEvent) { - var me = this; - // If the event is a touchstart, leave it until the click to focus - // A mousedown outside a cell. Must be in a Feature, or on a row border - if (!mousedownEvent.position.cellElement && (mousedownEvent.pointerType !== 'touch')) { - me.getClosestCell(mousedownEvent); - me.setPosition(mousedownEvent.position, null, mousedownEvent); - } - }, - onItemClick: function(view, record, item, index, clickEvent) { - // A mousedown outside a cell. Must be in a Feature, or on a row border - if (!clickEvent.position.cellElement) { - this.getClosestCell(clickEvent); - // touchstart does not focus the closest cell, leave it until touchend (translated as a click) - if (clickEvent.pointerType === 'touch') { - this.setPosition(clickEvent.position, null, clickEvent); - } - this.fireNavigateEvent(clickEvent); - } - }, - getClosestCell: function(event) { - var position = event.position, - targetCell = position.cellElement, - x, columns, len, i, column, b; - if (!targetCell) { - x = event.getX(); - columns = position.view.getVisibleColumnManager().getColumns(); - len = columns.length; - for (i = 0; i < len; i++) { - column = columns[i]; - b = columns[i].getBox(); - if (x >= b.left && x < b.right) { - position.setColumn(columns[i]); - position.rowElement = position.getRow(true); - position.cellElement = position.getCell(true); - return; - } - } - } - }, - deferSetPosition: function(delay, recordIndex, columnIndex, keyEvent, suppressEvent, preventNavigation) { - var setPositionTask = this.view.getFocusTask(); - // This is essentially a focus operation. Use the singleton focus task used by Focusable Components - // to schedule a setPosition call. This way it can be superseded programmatically by regular Component focus calls. - setPositionTask.delay(delay, this.setPosition, this, [ - recordIndex, - columnIndex, - keyEvent, - suppressEvent, - preventNavigation - ]); - return setPositionTask; - }, - setPosition: function(recordIndex, columnIndex, keyEvent, suppressEvent, preventNavigation) { - var me = this, - view, scroller, selModel, dataSource, columnManager, newRecordIndex, newColumnIndex, newRecord, newColumn, - clearing = recordIndex == null && columnIndex == null, - isClear = me.record == null && me.recordIndex == null && me.item == null; - // Work out the view we are operating on. - // If they passed a CellContext, use the view from that. - // Otherwise, use the view injected into the event by Ext.view.View#processEvent. - // Otherwise, use the last focused view. - // Failing that, use the view we were bound to. - if (recordIndex && recordIndex.isCellContext) { - view = recordIndex.view; - } else if (keyEvent && keyEvent.view) { - view = keyEvent.view; - } else if (me.lastFocused) { - view = me.lastFocused.view; - } else { - view = me.view; - } - // In case any async focus was requested before this call. - view.getFocusTask().cancel(); - // Return if the view was destroyed between the deferSetPosition call and now, or if the call is a no-op - // or if there are no items which could be focused. - if (view.destroyed || !view.refreshCounter || !view.ownerCt || clearing && isClear || !view.all.getCount()) { - return; - } - selModel = view.getSelectionModel(); - dataSource = view.dataSource; - columnManager = view.getVisibleColumnManager(); - // If a CellContext is passed, use it. - // Passing null happens on blur to remove focus class. - if (recordIndex && recordIndex.isCellContext) { - newRecord = recordIndex.record; - newRecordIndex = recordIndex.rowIdx; - newColumnIndex = recordIndex.colIdx; - newColumn = recordIndex.column; - // If the record being focused is not available (eg, after a removal), then go to the same position - if (dataSource.indexOf(newRecord) === -1) { - scroller = view.getScrollable(); - // Change recordIndex so that the "No movement" test is bypassed if the record is not found - me.recordIndex = -1; - // If the view will not jump upwards to bring the next row under the mouse as expected - // because it's at the end, focus the previous row - if (scroller.getPosition().y >= scroller.getMaxPosition().y - view.all.last(true).offsetHeight) { - recordIndex.rowIdx--; - } - newRecordIndex = Math.min(recordIndex.rowIdx, dataSource.getCount() - 1); - newColumnIndex = Math.min(newColumnIndex, columnManager.getColumns().length); - newRecord = dataSource.getAt(newRecordIndex); - newColumn = columnManager.getColumns()[newColumnIndex]; - } - } else { - // Both axes are null, we defocus - if (clearing) { - newRecord = newRecordIndex = null; - } else { - // AbstractView's default behaviour on focus is to call setPosition(0); - // A call like this should default to the last column focused, or column 0; - if (columnIndex == null) { - columnIndex = me.lastFocused ? me.lastFocused.column : 0; - } - if (typeof recordIndex === 'number') { - newRecordIndex = Math.max(Math.min(recordIndex, dataSource.getCount() - 1), 0); - newRecord = dataSource.getAt(recordIndex); - } - // row is a Record - else if (recordIndex.isEntity) { - newRecord = recordIndex; - newRecordIndex = dataSource.indexOf(newRecord); - } - // row is a grid row - else if (recordIndex.tagName) { - newRecord = view.getRecord(recordIndex); - newRecordIndex = dataSource.indexOf(newRecord); - if (newRecordIndex === -1) { - newRecord = null; - } - } else { - if (isClear) { - return; - } - clearing = true; - newRecord = newRecordIndex = null; - } - } - // Record position was successful - if (newRecord) { - // If the record being focused is not available (eg, after a sort), then go to 0,0 - if (newRecordIndex === -1) { - // Change recordIndex so that the "No movement" test is bypassed if the record is not found - me.recordIndex = -1; - newRecord = dataSource.getAt(0); - newRecordIndex = 0; - columnIndex = null; - } - // No columnIndex passed, and no previous column position - default to column 0 - if (columnIndex == null) { - if (!(newColumn = me.column)) { - newColumnIndex = 0; - newColumn = columnManager.getColumns()[0]; - } - } else if (typeof columnIndex === 'number') { - newColumn = columnManager.getColumns()[columnIndex]; - newColumnIndex = columnIndex; - } else { - newColumn = columnIndex; - newColumnIndex = columnManager.indexOf(columnIndex); - } - } else { - clearing = true; - newColumn = newColumnIndex = null; - } - } - // No movement; just ensure the correct item is focused and return early. - // Do not push current position into previous position, do not fire events. - if (newRecordIndex === me.recordIndex && newColumnIndex === me.columnIndex && view === me.position.view) { - return me.focusPosition(me.position); - } - if (me.cell) { - me.cell.removeCls(me.focusCls); - } - // Track the last position. - // Used by SelectionModels as the navigation "from" position. - me.previousRecordIndex = me.recordIndex; - me.previousRecord = me.record; - me.previousItem = me.item; - me.previousCell = me.cell; - me.previousColumn = me.column; - me.previousColumnIndex = me.columnIndex; - me.previousPosition = me.position.clone(); - // Track the last selectionStart position to correctly track ranges (i.e., SHIFT + selection). - me.selectionStart = selModel.selectionStart; - // Set our CellContext to the new position - me.position.setAll(view, me.recordIndex = newRecordIndex, me.columnIndex = newColumnIndex, me.record = newRecord, me.column = newColumn); - if (clearing) { - me.item = me.cell = null; - } else { - me.focusPosition(me.position, preventNavigation); - } - // Legacy API is that the SelectionModel fires focuschange events and the TableView fires rowfocus and cellfocus events. - if (!suppressEvent) { - selModel.fireEvent('focuschange', selModel, me.previousRecord, me.record); - view.fireEvent('rowfocus', me.record, me.item, me.recordIndex); - view.fireEvent('cellfocus', me.record, me.cell, me.position); - } - // If we have moved, fire an event - if (keyEvent && !preventNavigation && me.cell !== me.previousCell) { - me.fireNavigateEvent(keyEvent); - } - }, - /** - * @private - * Focuses the currently active position. - * This is used on view refresh and on replace. - * @return {undefined} - */ - focusPosition: function(position) { - var me = this, - view, row; - me.item = me.cell = null; - if (position && position.record && position.column) { - view = position.view; - // If the position is passed from a grid event, the rowElement will be stamped into it. - // Otherwise, select it from the indicated item. - if (position.rowElement) { - row = me.item = position.rowElement; - } else { - // Get the dataview item for the position's record - row = view.getRowByRecord(position.record); - } - // If there is no item at that index, it's probably because there's buffered rendering. - // This is handled below. - if (row) { - // If the position is passed from a grid event, the cellElement will be stamped into it. - // Otherwise, select it from the row. - me.cell = position.cellElement || Ext.fly(row).down(position.column.getCellSelector(), true); - // Maintain the cell as a Flyweight to avoid transient elements ending up in the cache as full Ext.Elements. - if (me.cell) { - me.cell = new Ext.dom.Fly(me.cell); - // Maintain lastFocused in the view so that on non-specific focus of the View, we can focus the view's correct descendant. - view.lastFocused = me.lastFocused = me.position.clone(); - me.focusItem(me.cell); - view.focusEl = me.cell; - } else // Cell no longer in view. Clear current position. - { - me.position.setAll(); - me.record = me.column = me.recordIndex = me.columnIndex = null; - } - } else // View node no longer in view. Clear current position. - // Attempt to scroll to the record if it is in the store, but out of rendered range. - { - row = view.dataSource.indexOf(position.record); - me.position.setAll(); - me.record = me.column = me.recordIndex = me.columnIndex = null; - // The reason why the row could not be selected from the DOM could be because it's - // out of rendered range, so scroll to the row, and then try focusing it. - if (row !== -1 && view.bufferedRenderer) { - me.lastKeyEvent = null; - view.bufferedRenderer.scrollTo(row, false, me.afterBufferedScrollTo, me); - } - } - } - }, - /** - * @template - * @protected - * Called to focus an item in the client {@link Ext.view.View DataView}. - * The default implementation adds the {@link #focusCls} to the passed item focuses it. - * Subclasses may choose to keep focus in another target. - * - * For example {@link Ext.view.BoundListKeyNav} maintains focus in the input field. - * @param {Ext.dom.Element} item - * @return {undefined} - */ - focusItem: function(item) { - item.addCls(this.focusCls); - item.focus(); - }, - getCell: function() { - return this.cell; - }, - getPosition: function(skipChecks) { - var me = this, - position = me.position, - curIndex, view, dataSource; - if (position.record && position.column) { - // If caller doesn't care whether the record and column is still there, just needs to know about focus - if (skipChecks) { - return position; - } - view = position.view; - dataSource = view.dataSource; - curIndex = dataSource.indexOf(position.record); - // If not with the same ID, at the same index if that is in range - if (curIndex === -1) { - curIndex = position.rowIdx; - // If no record now at that index (even if it's less than the totalCount, it may be a BufferedStore) - // then there is no focus position, and we must return null - if (!(position.record = dataSource.getAt(curIndex))) { - curIndex = -1; - } - } - // If the positioned record or column has gone away, we have no position - if (curIndex === -1 || view.getVisibleColumnManager().indexOf(position.column) === -1) { - position.setAll(); - me.record = me.column = me.recordIndex = me.columnIndex = null; - } else { - return position; - } - } - return null; - }, - getLastFocused: function() { - var me = this, - view, - lastFocused = me.lastFocused; - if (lastFocused && lastFocused.record && lastFocused.column) { - view = lastFocused.view; - // If the last focused record or column has gone away, we have no lastFocused - if (view.dataSource.indexOf(lastFocused.record) !== -1 && view.getVisibleColumnManager().indexOf(lastFocused.column) !== -1) { - return lastFocused; - } - } - }, - onKeyTab: function(keyEvent) { - var forward = !keyEvent.shiftKey, - position = keyEvent.position.clone(), - view = position.view, - cell = keyEvent.position.cellElement, - tabbableChildren = Ext.fly(cell).findTabbableElements(), - focusTarget, - actionables = view.ownerGrid.actionables, - len = actionables.length, - i; - // We control navigation when in actionable mode. - // no TAB events must navigate. - keyEvent.preventDefault(); - // Find the next or previous tabbable in this cell. - focusTarget = tabbableChildren[Ext.Array.indexOf(tabbableChildren, keyEvent.target) + (forward ? 1 : -1)]; - // If we are exiting the cell: - // Find next cell if possible, otherwise, we are exiting the row - while (!focusTarget && (cell = cell[forward ? 'nextSibling' : 'previousSibling'])) { - // Move position pointer to point to the new cell - position.setColumn(view.getHeaderByCell(cell)); - // Inform all Actionables that we intend to activate this cell. - // If they are actionable, they will show/insert tabbable elements in this cell. - for (i = 0; i < len; i++) { - actionables[i].activateCell(position); - } - // If there are now tabbable elements in this cell (entering a row restores tabbability) - // and Actionables also show/insert tabbables), then focus in the current direction. - if ((tabbableChildren = Ext.fly(cell).findTabbableElements()).length) { - focusTarget = tabbableChildren[forward ? 0 : tabbableChildren.length - 1]; - } - } - // We found a focus target either in the cell or in a sibling cell in the direction of navigation. - if (focusTarget) { - // Keep actionPosition synched - this.actionPosition = position.view.actionPosition = position; - Ext.fly(focusTarget).focus(); - return; - } - // IE, which does not blur on removal from DOM of focused element must be kicked to blur the focused element - // which Actionables react to. - if (Ext.isIE) { - view.el.focus(); - } - // We need to exit the row - view.onRowExit(keyEvent.item, keyEvent.item[forward ? 'nextSibling' : 'previousSibling'], forward); - }, - onKeyUp: function(keyEvent) { - var newRecord = keyEvent.view.walkRecs(keyEvent.record, -1), - pos = this.getPosition(); - if (newRecord) { - pos.setRow(newRecord); - // If no cell at the current column, move towards row start - if (!pos.getCell(true)) { - pos.navigate(-1); - } - this.setPosition(pos, null, keyEvent); - } - }, - onKeyDown: function(keyEvent) { - // If we are in the middle of an animated node expand, jump to next sibling. - // The first child record is in a temp animation DIV and will be removed, so will blur. - var newRecord = keyEvent.record.isExpandingOrCollapsing ? null : keyEvent.view.walkRecs(keyEvent.record, 1), - pos = this.getPosition(); - if (newRecord) { - pos.setRow(newRecord); - // If no cell at the current column, move towards row start - if (!pos.getCell(true)) { - pos.navigate(-1); - } - this.setPosition(pos, null, keyEvent); - } - }, - onKeyRight: function(keyEvent) { - var newPosition = this.move('right', keyEvent); - if (newPosition) { - this.setPosition(newPosition, null, keyEvent); - } - }, - onKeyLeft: function(keyEvent) { - var newPosition = this.move('left', keyEvent); - if (newPosition) { - this.setPosition(newPosition, null, keyEvent); - } - }, - // ENTER emulates a dblclick event at the TableView level - onKeyEnter: function(keyEvent) { - var eventArgs = [ - 'cellclick', - keyEvent.view, - keyEvent.position.cellElement, - keyEvent.position.colIdx, - keyEvent.record, - keyEvent.position.rowElement, - keyEvent.recordIndex, - keyEvent - ], - actionCell = keyEvent.position.getCell(); - // May have been deleted by now by an ActionColumn handler - if (actionCell) { - // Stop the keydown event so that an ENTER keyup does not get delivered to - // any element which focus is transferred to in a click handler. - if (!actionCell.query('[tabIndex="-1"]').length) { - keyEvent.stopEvent(); - keyEvent.view.fireEvent.apply(keyEvent.view, eventArgs); - eventArgs[0] = 'celldblclick'; - keyEvent.view.fireEvent.apply(keyEvent.view, eventArgs); - } - // Enters actionable mode. Unless the emulated evenbts have done it - if (!this.view.actionableMode) { - this.view.ownerGrid.setActionableMode(true, this.getPosition()); - } - } - }, - onKeyF2: function(keyEvent) { - // Toggles actionable mode - var grid = this.view.ownerGrid, - actionableMode = grid.actionableMode; - grid.setActionableMode(!actionableMode, actionableMode ? null : this.getPosition()); - }, - onKeyEsc: function(keyEvent) { - // Exits actionable mode - this.view.ownerGrid.setActionableMode(false); - }, - move: function(dir, keyEvent) { - var me = this, - position = me.getPosition(); - if (position && position.record) { - // Do not allow SHIFT+(left|right) to wrap. - return position.view.walkCells(position, dir, keyEvent.shiftKey && (dir === 'right' || dir === 'left') ? me.vetoRowChange : null, me); - } - // Enforce code correctness in unbuilt source. - return null; - }, - vetoRowChange: function(newPosition) { - return this.getPosition().record === newPosition.record; - }, - // Go one page down from the lastFocused record in the grid. - onKeyPageDown: function(keyEvent) { - var me = this, - view = keyEvent.view, - rowsVisible = me.getRowsVisible(), - newIdx, newRecord; - if (rowsVisible) { - // If rendering is buffered, we cannot just increment the row - the row may not be there - // We have to ask the BufferedRenderer to navigate to the target. - // And that may involve asynchronous I/O, so must post-process in a callback. - if (view.bufferedRenderer) { - newIdx = Math.min(keyEvent.recordIndex + rowsVisible, view.dataSource.getCount() - 1); - me.lastKeyEvent = keyEvent; - view.bufferedRenderer.scrollTo(newIdx, false, me.afterBufferedScrollTo, me); - } else { - newRecord = view.walkRecs(keyEvent.record, rowsVisible); - me.setPosition(newRecord, null, keyEvent); - } - } - }, - // Go one page up from the lastFocused record in the grid. - onKeyPageUp: function(keyEvent) { - var me = this, - view = keyEvent.view, - rowsVisible = me.getRowsVisible(), - newIdx, newRecord; - if (rowsVisible) { - // If rendering is buffered, we cannot just increment the row - the row may not be there - // We have to ask the BufferedRenderer to navigate to the target. - // And that may involve asynchronous I/O, so must post-process in a callback. - if (view.bufferedRenderer) { - newIdx = Math.max(keyEvent.recordIndex - rowsVisible, 0); - me.lastKeyEvent = keyEvent; - view.bufferedRenderer.scrollTo(newIdx, false, me.afterBufferedScrollTo, me); - } else { - newRecord = view.walkRecs(keyEvent.record, -rowsVisible); - me.setPosition(newRecord, null, keyEvent); - } - } - }, - // Home moves the focus to the first cell of the current row. - onKeyHome: function(keyEvent) { - var me = this, - view = keyEvent.view; - // ALT+Home - go to first visible record in grid. - if (keyEvent.altKey) { - if (view.bufferedRenderer) { - // If rendering is buffered, we cannot just increment the row - the row may not be there - // We have to ask the BufferedRenderer to navigate to the target. - // And that may involve asynchronous I/O, so must post-process in a callback. - me.lastKeyEvent = keyEvent; - view.bufferedRenderer.scrollTo(0, false, me.afterBufferedScrollTo, me); - } else { - // Walk forwards to the first record - me.setPosition(view.walkRecs(keyEvent.record, -view.dataSource.indexOf(keyEvent.record)), null, keyEvent); - } - } else // Home moves the focus to the First cell in the current row. - { - me.setPosition(keyEvent.record, 0, keyEvent); - } - }, - afterBufferedScrollTo: function(newIdx, newRecord) { - this.setPosition(newRecord, null, this.lastKeyEvent, null, !this.lastKeyEvent); - }, - // End moves the focus to the last cell in the current row. - onKeyEnd: function(keyEvent) { - var me = this, - view = keyEvent.view; - // ALT/End - go to last visible record in grid. - if (keyEvent.altKey) { - if (view.bufferedRenderer) { - // If rendering is buffered, we cannot just increment the row - the row may not be there - // We have to ask the BufferedRenderer to navigate to the target. - // And that may involve asynchronous I/O, so must postprocess in a callback. - me.lastKeyEvent = keyEvent; - view.bufferedRenderer.scrollTo(view.store.getCount() - 1, false, me.afterBufferedScrollTo, me); - } else { - // Walk forwards to the end record - me.setPosition(view.walkRecs(keyEvent.record, view.dataSource.getCount() - 1 - view.dataSource.indexOf(keyEvent.record)), null, keyEvent); - } - } else // End moves the focus to the last cell in the current row. - { - me.setPosition(keyEvent.record, keyEvent.view.getVisibleColumnManager().getColumns().length - 1, keyEvent); - } - }, - // Returns the number of rows currently visible on the screen or - // false if there were no rows. This assumes that all rows are - // of the same height and the first view is accurate. - getRowsVisible: function() { - var rowsVisible = false, - view = this.view, - firstRow = view.all.first(), - rowHeight, gridViewHeight; - if (firstRow) { - rowHeight = firstRow.getHeight(); - gridViewHeight = view.el.getHeight(); - rowsVisible = Math.floor(gridViewHeight / rowHeight); - } - return rowsVisible; - }, - fireNavigateEvent: function(keyEvent) { - var me = this; - me.fireEvent('navigate', { - view: me.position.view, - navigationModel: me, - keyEvent: keyEvent || new Ext.event.Event({}), - previousPosition: me.previousPosition, - previousRecordIndex: me.previousRecordIndex, - previousRecord: me.previousRecord, - previousItem: me.previousItem, - previousCell: me.previousCell, - previousColumnIndex: me.previousColumnIndex, - previousColumn: me.previousColumn, - position: me.position, - recordIndex: me.recordIndex, - record: me.record, - selectionStart: me.selectionStart, - item: me.item, - cell: me.cell, - columnIndex: me.columnIndex, - column: me.column - }); - } -}); - -/** - * Component layout for {@link Ext.view.Table} - * @private - * - */ -Ext.define('Ext.view.TableLayout', { - extend: 'Ext.layout.component.Auto', - alias: 'layout.tableview', - type: 'tableview', - beginLayout: function(ownerContext) { - var me = this, - owner = me.owner, - ownerGrid = owner.ownerGrid, - partner = owner.lockingPartner, - context = ownerContext.context; - // Flag whether we need to do row height synchronization - ownerContext.doSyncRowHeights = partner && partner.grid.isVisible() && ownerGrid.syncRowHeight; - if (!me.columnFlusherId) { - me.columnFlusherId = me.id + '-columns'; - me.rowHeightFlusherId = me.id + '-rows'; - } - if (me.owner.bufferedRenderer) { - me.owner.bufferedRenderer.beforeTableLayout(ownerContext); - } - me.callParent([ - ownerContext - ]); - // If we are in a twinned grid (locked view) then set up bidirectional links with - // the other side's layout context. If the locked or normal side is hidden then - // we should treat it as thoguh we were laying out a single grid, so don't setup the partners. - // This is typically if a grid is configured with locking but starts with no locked columns. - if (ownerContext.doSyncRowHeights) { - if (!ownerContext.lockingPartnerContext) { - (ownerContext.lockingPartnerContext = context.getCmp(partner)).lockingPartnerContext = ownerContext; - } - ownerContext.rowHeightSynchronizer = me.owner.syncRowHeightBegin(); - } - // Grab a ContextItem for the header container (and make sure the TableLayout can - // reach us as well): - (ownerContext.headerContext = context.getCmp(me.headerCt)).viewContext = ownerContext; - }, - beginLayoutCycle: function(ownerContext, firstCycle) { - this.callParent([ - ownerContext, - firstCycle - ]); - if (ownerContext.syncRowHeights) { - ownerContext.target.syncRowHeightClear(ownerContext.rowHeightSynchronizer); - ownerContext.syncRowHeights = false; - } - }, - calculate: function(ownerContext) { - var me = this, - context = ownerContext.context, - lockingPartnerContext = ownerContext.lockingPartnerContext, - headerContext = ownerContext.headerContext, - ownerCtContext = ownerContext.ownerCtContext, - owner = me.owner, - columnsChanged = headerContext.getProp('columnsChanged'), - state = ownerContext.state, - columnFlusher, otherSynchronizer, synchronizer, rowHeightFlusher, - bodyDom = owner.body.dom, - bodyHeight, ctSize, overflowY; - // Shortcut when empty grid - let the base handle it. - // EXTJS-14844: Even when no data rows (all.getCount() === 0) there may be summary rows to size. - if (!owner.all.getCount() && (!bodyDom || !owner.body.child('table'))) { - ownerContext.setProp('viewOverflowY', false); - me.callParent([ - ownerContext - ]); - return; - } - if (columnsChanged === undefined) { - // We cannot proceed when we have rows but no columnWidths determined... - me.done = false; - return; - } - if (columnsChanged) { - if (!(columnFlusher = state.columnFlusher)) { - // Since the columns have changed, we need to write the widths to the DOM. - context.queueFlush(state.columnFlusher = columnFlusher = { - ownerContext: ownerContext, - columnsChanged: columnsChanged, - layout: me, - id: me.columnFlusherId, - flush: me.flushColumnWidths - }); - } - if (!columnFlusher.flushed) { - // We have queued the columns to be written, but they are still pending, so - // we cannot proceed. - me.done = false; - return; - } - } - // They have to turn row height synchronization on, or there may be variable row heights - // Either no columns changed, or we have flushed those changes.. which means the - // column widths in the DOM are correct. Now we can proceed to syncRowHeights (if - // we are locking) or wrap it up by determining our vertical overflow. - if (ownerContext.doSyncRowHeights) { - if (!(rowHeightFlusher = state.rowHeightFlusher)) { - // When we are locking, both sides need to read their row heights in a read - // phase (i.e., right now). - if (!(synchronizer = state.rowHeights)) { - state.rowHeights = synchronizer = ownerContext.rowHeightSynchronizer; - me.owner.syncRowHeightMeasure(synchronizer); - ownerContext.setProp('rowHeights', synchronizer); - } - if (!(otherSynchronizer = lockingPartnerContext.getProp('rowHeights'))) { - me.done = false; - return; - } - context.queueFlush(state.rowHeightFlusher = rowHeightFlusher = { - ownerContext: ownerContext, - synchronizer: synchronizer, - otherSynchronizer: otherSynchronizer, - layout: me, - id: me.rowHeightFlusherId, - flush: me.flushRowHeights - }); - } - if (!rowHeightFlusher.flushed) { - me.done = false; - return; - } - } - me.callParent([ - ownerContext - ]); - if (!ownerContext.heightModel.shrinkWrap) { - // If the grid is shrink wrapping, we can't be overflowing - overflowY = false; - if (!ownerCtContext.heightModel.shrinkWrap) { - // We are placed in a fit layout of the gridpanel (our ownerCt), so we need to - // consult its containerSize when we are not shrink-wrapping to see if our - // content will overflow vertically. - ctSize = ownerCtContext.target.layout.getContainerSize(ownerCtContext); - if (!ctSize.gotHeight) { - me.done = false; - return; - } - bodyHeight = bodyDom.offsetHeight; - overflowY = bodyHeight > ctSize.height; - } - ownerContext.setProp('viewOverflowY', overflowY); - } - }, - measureContentHeight: function(ownerContext) { - var owner = this.owner, - bodyDom = owner.body.dom, - emptyEl = owner.emptyEl, - bodyHeight = 0; - if (emptyEl) { - bodyHeight += emptyEl.offsetHeight; - } - if (bodyDom) { - bodyHeight += bodyDom.offsetHeight; - } - // This will have been figured out by now because the columnWidths have been - // published... - if (ownerContext.headerContext.state.boxPlan.tooNarrow) { - bodyHeight += Ext.getScrollbarSize().height; - } - return bodyHeight; - }, - flushColumnWidths: function() { - // NOTE: The "this" pointer here is the flusher object that was queued. - var flusher = this, - me = flusher.layout, - ownerContext = flusher.ownerContext, - columnsChanged = flusher.columnsChanged, - owner = ownerContext.target, - len = columnsChanged.length, - column, i, colWidth, lastBox; - if (ownerContext.state.columnFlusher !== flusher) { - return; - } - // Set column width corresponding to each header - for (i = 0; i < len; i++) { - if (!(column = columnsChanged[i])) { - - continue; - } - colWidth = column.props.width; - owner.body.select(owner.getColumnSizerSelector(column.target)).setWidth(colWidth); - // Enable the next go-round of headerCt's ColumnLayout change check to - // read true, flushed lastBox widths that are in the Table - lastBox = column.lastBox; - if (lastBox) { - lastBox.width = colWidth; - } - } - flusher.flushed = true; - if (!me.pending) { - ownerContext.context.queueLayout(me); - } - }, - flushRowHeights: function() { - // NOTE: The "this" pointer here is the flusher object that was queued. - var flusher = this, - me = flusher.layout, - ownerContext = flusher.ownerContext; - if (ownerContext.state.rowHeightFlusher !== flusher) { - return; - } - ownerContext.target.syncRowHeightFinish(flusher.synchronizer, flusher.otherSynchronizer); - flusher.flushed = true; - ownerContext.syncRowHeights = true; - if (!me.pending) { - ownerContext.context.queueLayout(me); - } - }, - finishedLayout: function(ownerContext) { - var me = this, - nodeContainer = Ext.fly(me.owner.getNodeContainer()); - me.callParent([ - ownerContext - ]); - if (nodeContainer) { - nodeContainer.setWidth(ownerContext.headerContext.props.contentWidth); - } - // Inform any buffered renderer about completion of the layout of its view - if (me.owner.bufferedRenderer) { - me.owner.bufferedRenderer.afterTableLayout(ownerContext); - } - } -}); - -/** - * @private - */ -Ext.define('Ext.grid.locking.RowSynchronizer', { - constructor: function(view, rowEl) { - var me = this, - rowTpl; - me.view = view; - me.rowEl = rowEl; - me.els = {}; - me.add('data', view.rowSelector); - for (rowTpl = view.rowTpl; rowTpl; rowTpl = rowTpl.nextTpl) { - if (rowTpl.beginRowSync) { - rowTpl.beginRowSync(me); - } - } - }, - add: function(name, selector) { - var el = Ext.fly(this.rowEl).down(selector, true); - if (el) { - this.els[name] = { - el: el - }; - } - }, - finish: function(other) { - var me = this, - els = me.els, - otherEls = other.els, - otherEl, - growth = 0, - otherGrowth = 0, - delta, name, otherHeight; - for (name in els) { - otherEl = otherEls[name]; - // Partnet RowSynchronizer may not have the element. - // For example, group summary may not be wanted in locking side. - otherHeight = otherEl ? otherEl.height : 0; - delta = otherHeight - els[name].height; - if (delta > 0) { - growth += delta; - Ext.fly(els[name].el).setHeight(otherHeight); - } else { - otherGrowth -= delta; - } - } - // Compare the growth to both rows and see if this row is lacking. - otherHeight = other.rowHeight + otherGrowth; - // IE9 uses content box sizing on table, so height must not include border - if (Ext.isIE9 && me.view.ownerGrid.rowLines) { - otherHeight--; - } - if (me.rowHeight + growth < otherHeight) { - Ext.fly(me.rowEl).setHeight(otherHeight); - } - }, - measure: function() { - var me = this, - els = me.els, - name; - me.rowHeight = me.rowEl.offsetHeight; - for (name in els) { - els[name].height = els[name].el.offsetHeight; - } - }, - reset: function() { - var els = this.els, - name; - this.rowEl.style.height = ''; - for (name in els) { - els[name].el.style.height = ''; - } - } -}); - -/** - * @private - * A cache of View elements keyed using the index of the associated record in the store. - * - * This implements the methods of {Ext.dom.CompositeElement} which are used by {@link Ext.view.AbstractView} - * to provide a map of record nodes and methods to manipulate the nodes. - * @class Ext.view.NodeCache - */ -Ext.define('Ext.view.NodeCache', { - requires: [ - 'Ext.dom.CompositeElementLite' - ], - statics: { - range: document.createRange && document.createRange() - }, - constructor: function(view) { - this.view = view; - this.clear(); - this.el = new Ext.dom.Fly(); - }, - destroy: function() { - var me = this; - if (!me.destroyed) { - me.el.destroy(); - me.el = me.view = null; - me.destroyed = true; - } - me.callParent(); - }, - /** - * Removes all elements from this NodeCache. - * @param {Boolean} [removeDom] True to also remove the elements from the document. - */ - clear: function(removeDom) { - var me = this, - elements = me.elements, - range = me.statics().range, - key; - if (me.count && removeDom) { - if (range) { - range.setStartBefore(elements[me.startIndex]); - range.setEndAfter(elements[me.endIndex]); - range.deleteContents(); - } else { - for (key in elements) { - Ext.removeNode(elements[key]); - } - } - } - me.elements = {}; - me.count = me.startIndex = 0; - me.endIndex = -1; - }, - /** - * Clears this NodeCache and adds the elements passed. - * @param {HTMLElement[]} els An array of DOM elements from which to fill this NodeCache. - * @return {Ext.view.NodeCache} this - */ - fill: function(newElements, startIndex, fixedNodes) { - fixedNodes = fixedNodes || 0; - var me = this, - elements = me.elements = {}, - i, - len = newElements.length - fixedNodes; - if (!startIndex) { - startIndex = 0; - } - for (i = 0; i < len; i++) { - elements[startIndex + i] = newElements[i + fixedNodes]; - } - me.startIndex = startIndex; - me.endIndex = startIndex + len - 1; - me.count = len; - return this; - }, - insert: function(insertPoint, nodes) { - var me = this, - elements = me.elements, - i, - nodeCount = nodes.length; - // If not inserting into empty cache, validate, and possibly shuffle. - if (me.count) { - if (insertPoint > me.endIndex + 1 || insertPoint + nodes.length - 1 < me.startIndex) { - Ext.raise('Discontiguous range would result from inserting ' + nodes.length + ' nodes at ' + insertPoint); - } - // Move following nodes forwards by positions - if (insertPoint < me.count) { - for (i = me.endIndex + nodeCount; i >= insertPoint + nodeCount; i--) { - elements[i] = elements[i - nodeCount]; - elements[i].setAttribute('data-recordIndex', i); - } - } - me.endIndex = me.endIndex + nodeCount; - } else // Empty cache. set up counters - { - me.startIndex = insertPoint; - me.endIndex = insertPoint + nodeCount - 1; - } - // Insert new nodes into place - for (i = 0; i < nodeCount; i++ , insertPoint++) { - elements[insertPoint] = nodes[i]; - elements[insertPoint].setAttribute('data-recordIndex', insertPoint); - } - me.count += nodeCount; - }, - invoke: function(fn, args) { - var me = this, - element, i; - fn = Ext.dom.Element.prototype[fn]; - for (i = me.startIndex; i <= me.endIndex; i++) { - element = me.item(i); - if (element) { - fn.apply(element, args); - } - } - return me; - }, - item: function(index, asDom) { - var el = this.elements[index], - result = null; - if (el) { - result = asDom ? this.elements[index] : this.el.attach(this.elements[index]); - } - return result; - }, - first: function(asDom) { - return this.item(this.startIndex, asDom); - }, - last: function(asDom) { - return this.item(this.endIndex, asDom); - }, - /** - * @private - * Used by buffered renderer when adding or removing record ranges which are above the - * rendered block. The element block must be shuffled up or down the index range, - * and the data-recordIndex connector attribute must be updated. - * - */ - moveBlock: function(increment) { - var me = this, - elements = me.elements, - node, end, step, i; - // No movement; return - if (!increment) { - return; - } - if (increment < 0) { - i = me.startIndex - 1; - end = me.endIndex; - step = 1; - } else { - i = me.endIndex + 1; - end = me.startIndex; - step = -1; - } - me.startIndex += increment; - me.endIndex += increment; - do { - i += step; - node = elements[i + increment] = elements[i]; - node.setAttribute('data-recordIndex', i + increment); - if (i < me.startIndex || i > me.endIndex) { - delete elements[i]; - } - } while (// "from" element is outside of the new range, then delete it. - i !== end); - delete elements[i]; - }, - getCount: function() { - return this.count; - }, - slice: function(start, end) { - var elements = this.elements, - result = [], - i; - if (!end) { - end = this.endIndex; - } else { - end = Math.min(this.endIndex, end - 1); - } - for (i = start || this.startIndex; i <= end; i++) { - result.push(elements[i]); - } - return result; - }, - /** - * Replaces the specified element with the passed element. - * @param {String/HTMLElement/Ext.dom.Element/Number} el The id of an element, the Element itself, the index of the - * element in this composite to replace. - * @param {String/Ext.dom.Element} replacement The id of an element or the Element itself. - * @param {Boolean} [domReplace] True to remove and replace the element in the document too. - */ - replaceElement: function(el, replacement, domReplace) { - var elements = this.elements, - index = (typeof el === 'number') ? el : this.indexOf(el); - if (index > -1) { - replacement = Ext.getDom(replacement); - if (domReplace) { - el = elements[index]; - el.parentNode.insertBefore(replacement, el); - Ext.removeNode(el); - replacement.setAttribute('data-recordIndex', index); - } - this.elements[index] = replacement; - } - return this; - }, - /** - * Find the index of the passed element within the composite collection. - * @param {String/HTMLElement/Ext.dom.Element/Number} el The id of an element, or an Ext.dom.Element, or an HTMLElement - * to find within the composite collection. - * @return {Number} The index of the passed Ext.dom.Element in the composite collection, or -1 if not found. - */ - indexOf: function(el) { - var elements = this.elements, - index; - el = Ext.getDom(el); - for (index = this.startIndex; index <= this.endIndex; index++) { - if (elements[index] === el) { - return index; - } - } - return -1; - }, - removeRange: function(start, end, removeDom) { - var me = this, - elements = me.elements, - removed = [], - el, i, removeCount, fromPos; - if (end == null) { - end = me.endIndex + 1; - } else { - end = Math.min(me.endIndex + 1, end + 1); - } - if (start == null) { - start = me.startIndex; - } - removeCount = end - start; - for (i = start , fromPos = end; i <= me.endIndex; i++ , fromPos++) { - el = elements[i]; - // Within removal range and we are removing from DOM - if (i < end) { - removed.push(el); - if (removeDom) { - Ext.removeNode(el); - } - } - // If the from position is occupied, shuffle that entry back into reference "i" - if (fromPos <= me.endIndex) { - el = elements[i] = elements[fromPos]; - el.setAttribute('data-recordIndex', i); - } else // The from position has walked off the end, so delete reference "i" - { - delete elements[i]; - } - } - me.count -= removeCount; - me.endIndex -= removeCount; - return removed; - }, - /** - * Removes the specified element(s). - * @param {String/HTMLElement/Ext.dom.Element/Number} el The id of an element, the Element itself, the index of the - * element in this composite or an array of any of those. - * @param {Boolean} [removeDom] True to also remove the element from the document - */ - removeElement: function(keys, removeDom) { - var me = this, - inKeys, key, - elements = me.elements, - el, deleteCount, - keyIndex = 0, - index, fromIndex; - // Sort the keys into ascending order so that we can iterate through the elements - // collection, and delete items encountered in the keys array as we encounter them. - if (Ext.isArray(keys)) { - inKeys = keys; - keys = []; - deleteCount = inKeys.length; - for (keyIndex = 0; keyIndex < deleteCount; keyIndex++) { - key = inKeys[keyIndex]; - if (typeof key !== 'number') { - key = me.indexOf(key); - } - // Could be asked to remove data above the start, or below the end of rendered zone in a buffer rendered view - // So only collect keys which are within our range - if (key >= me.startIndex && key <= me.endIndex) { - keys[keys.length] = key; - } - } - Ext.Array.sort(keys); - deleteCount = keys.length; - } else { - // Could be asked to remove data above the start, or below the end of rendered zone in a buffer rendered view - if (keys < me.startIndex || keys > me.endIndex) { - return; - } - deleteCount = 1; - keys = [ - keys - ]; - } - // Iterate through elements starting at the element referenced by the first deletion key. - // We also start off and index zero in the keys to delete array. - for (index = fromIndex = keys[0] , keyIndex = 0; index <= me.endIndex; index++ , fromIndex++) { - // If the current index matches the next key in the delete keys array, this - // entry is being deleted, so increment the fromIndex to skip it. - // Advance to next entry in keys array. - if (keyIndex < deleteCount && index === keys[keyIndex]) { - fromIndex++; - keyIndex++; - if (removeDom) { - Ext.removeNode(elements[index]); - } - } - // Shuffle entries forward of the delete range back into contiguity. - if (fromIndex <= me.endIndex && fromIndex >= me.startIndex) { - el = elements[index] = elements[fromIndex]; - el.setAttribute('data-recordIndex', index); - } else { - delete elements[index]; - } - } - me.endIndex -= deleteCount; - me.count -= deleteCount; - }, - /** - * Appends/prepends records depending on direction flag - * @param {Ext.data.Model[]} newRecords Items to append/prepend - * @param {Number} direction `-1' = scroll up, `0` = scroll down. - * @param {Number} removeCount The number of records to remove from the end. if scrolling - * down, rows are removed from the top and the new rows are added at the bottom. - * @return {HTMLElement[]} The view item nodes added either at the top or the bottom of the view. - */ - scroll: function(newRecords, direction, removeCount) { - var me = this, - view = me.view, - store = view.store, - elements = me.elements, - recCount = newRecords.length, - nodeContainer = view.getNodeContainer(), - fireItemRemove = view.hasListeners.itemremove, - fireItemAdd = view.hasListeners.itemadd, - range = me.statics().range, - i, el, removeEnd, children, result, removeStart, removedRecords, removedItems; - if (!newRecords.length) { - return; - } - // Scrolling up (content moved down - new content needed at top, remove from bottom) - if (direction === -1) { - if (removeCount) { - if (fireItemRemove) { - removedRecords = []; - removedItems = []; - } - removeStart = (me.endIndex - removeCount) + 1; - if (range) { - range.setStartBefore(elements[removeStart]); - range.setEndAfter(elements[me.endIndex]); - range.deleteContents(); - for (i = removeStart; i <= me.endIndex; i++) { - el = elements[i]; - delete elements[i]; - if (fireItemRemove) { - removedRecords.push(store.getByInternalId(el.getAttribute('data-recordId'))); - removedItems.push(el); - } - } - } else { - for (i = removeStart; i <= me.endIndex; i++) { - el = elements[i]; - delete elements[i]; - Ext.removeNode(el); - if (fireItemRemove) { - removedRecords.push(store.getByInternalId(el.getAttribute('data-recordId'))); - removedItems.push(el); - } - } - } - view.fireEvent('itemremove', removedRecords, removeStart, removedItems, view); - me.endIndex -= removeCount; - } - // Only do rendering if there are rows to render. - // This could have been a remove only operation due to a view resize event. - if (newRecords.length) { - // grab all nodes rendered, not just the data rows - result = view.bufferRender(newRecords, me.startIndex -= recCount); - children = result.children; - for (i = 0; i < recCount; i++) { - elements[me.startIndex + i] = children[i]; - } - nodeContainer.insertBefore(result.fragment, nodeContainer.firstChild); - // pass the new DOM to any interested parties - if (fireItemAdd) { - view.fireEvent('itemadd', newRecords, me.startIndex, children); - } - } - } else // Scrolling down (content moved up - new content needed at bottom, remove from top) - { - if (removeCount) { - if (fireItemRemove) { - removedRecords = []; - removedItems = []; - } - removeEnd = me.startIndex + removeCount; - if (range) { - range.setStartBefore(elements[me.startIndex]); - range.setEndAfter(elements[removeEnd - 1]); - range.deleteContents(); - for (i = me.startIndex; i < removeEnd; i++) { - el = elements[i]; - delete elements[i]; - if (fireItemRemove) { - removedRecords.push(store.getByInternalId(el.getAttribute('data-recordId'))); - removedItems.push(el); - } - } - } else { - for (i = me.startIndex; i < removeEnd; i++) { - el = elements[i]; - delete elements[i]; - Ext.removeNode(el); - if (fireItemRemove) { - removedRecords.push(store.getByInternalId(el.getAttribute('data-recordId'))); - removedItems.push(el); - } - } - } - view.fireEvent('itemremove', removedRecords, me.startIndex, removedItems, view); - me.startIndex = removeEnd; - } - // grab all nodes rendered, not just the data rows - result = view.bufferRender(newRecords, me.endIndex + 1); - children = result.children; - for (i = 0; i < recCount; i++) { - elements[me.endIndex += 1] = children[i]; - } - nodeContainer.appendChild(result.fragment); - // pass the new DOM to any interested parties - if (fireItemAdd) { - view.fireEvent('itemadd', newRecords, me.endIndex + 1, children); - } - } - // Keep count consistent. - me.count = me.endIndex - me.startIndex + 1; - return children; - }, - sumHeights: function() { - var result = 0, - elements = this.elements, - i; - for (i = this.startIndex; i <= this.endIndex; i++) { - result += elements[i].offsetHeight; - } - return result; - } -}, function() { - Ext.dom.CompositeElementLite.importElementMethods.call(this); -}); - -/** - * This class encapsulates the user interface for a tabular data set. - * It acts as a centralized manager for controlling the various interface - * elements of the view. This includes handling events, such as row and cell - * level based DOM events. It also reacts to events from the underlying {@link Ext.selection.Model} - * to provide visual feedback to the user. - * - * This class does not provide ways to manipulate the underlying data of the configured - * {@link Ext.data.Store}. - * - * This is the base class for both {@link Ext.grid.View} and {@link Ext.tree.View} and is not - * to be used directly. - */ -Ext.define('Ext.view.Table', { - extend: 'Ext.view.View', - xtype: [ - 'tableview', - 'gridview' - ], - alternateClassName: 'Ext.grid.View', - requires: [ - 'Ext.grid.CellContext', - 'Ext.view.TableLayout', - 'Ext.grid.locking.RowSynchronizer', - 'Ext.view.NodeCache', - 'Ext.util.DelayedTask', - 'Ext.util.MixedCollection' - ], - /** - * @property {Boolean} - * `true` in this class to identify an object as an instantiated Ext.view.TableView, or subclass thereof. - */ - isTableView: true, - config: { - selectionModel: { - type: 'rowmodel' - } - }, - inheritableStatics: { - // Events a TableView may fire. Used by Ext.grid.locking.View to relay events to its ownerGrid - // in order to quack like a genuine Ext.table.View. - // - // The events below are to be relayed only from the normal side view because the events - // are relayed from the selection model, so both sides will fire them. - /** - * @private - * @static - * @inheritable - */ - normalSideEvents: [ - "deselect", - "select", - "beforedeselect", - "beforeselect", - "selectionchange" - ], - // These events are relayed from both views because they are fired independently. - /** - * @private - * @static - * @inheritable - */ - events: [ - "blur", - "focus", - "move", - "resize", - "destroy", - "beforedestroy", - "boxready", - "afterrender", - "render", - "beforerender", - "removed", - "hide", - "beforehide", - "show", - "beforeshow", - "enable", - "disable", - "added", - "deactivate", - "beforedeactivate", - "activate", - "beforeactivate", - "cellkeydown", - "beforecellkeydown", - "cellmouseup", - "beforecellmouseup", - "cellmousedown", - "beforecellmousedown", - "cellcontextmenu", - "beforecellcontextmenu", - "celldblclick", - "beforecelldblclick", - "cellclick", - "beforecellclick", - "refresh", - "itemremove", - "itemadd", - "beforeitemupdate", - "itemupdate", - "viewready", - "beforerefresh", - "unhighlightitem", - "highlightitem", - "focuschange", - "containerkeydown", - "containercontextmenu", - "containerdblclick", - "containerclick", - "containermouseout", - "containermouseover", - "containermouseup", - "containermousedown", - "beforecontainerkeydown", - "beforecontainercontextmenu", - "beforecontainerdblclick", - "beforecontainerclick", - "beforecontainermouseout", - "beforecontainermouseover", - "beforecontainermouseup", - "beforecontainermousedown", - "itemkeydown", - "itemcontextmenu", - "itemdblclick", - "itemclick", - "itemmouseleave", - "itemmouseenter", - "itemmouseup", - "itemmousedown", - "rowclick", - "rowcontextmenu", - "rowdblclick", - "rowkeydown", - "rowmouseup", - "rowmousedown", - "rowkeydown", - "beforeitemkeydown", - "beforeitemcontextmenu", - "beforeitemdblclick", - "beforeitemclick", - "beforeitemmouseleave", - "beforeitemmouseenter", - "beforeitemmouseup", - "beforeitemmousedown", - "statesave", - "beforestatesave", - "staterestore", - "beforestaterestore", - "uievent", - "groupcollapse", - "groupexpand", - "scroll" - ] - }, - scrollable: true, - componentLayout: 'tableview', - baseCls: Ext.baseCSSPrefix + 'grid-view', - unselectableCls: Ext.baseCSSPrefix + 'unselectable', - /** - * @cfg {String} [firstCls='x-grid-cell-first'] - * A CSS class to add to the *first* cell in every row to enable special styling for the first column. - * If no styling is needed on the first column, this may be configured as `null`. - */ - firstCls: Ext.baseCSSPrefix + 'grid-cell-first', - /** - * @cfg {String} [lastCls='x-grid-cell-last'] - * A CSS class to add to the *last* cell in every row to enable special styling for the last column. - * If no styling is needed on the last column, this may be configured as `null`. - */ - lastCls: Ext.baseCSSPrefix + 'grid-cell-last', - itemCls: Ext.baseCSSPrefix + 'grid-item', - selectedItemCls: Ext.baseCSSPrefix + 'grid-item-selected', - selectedCellCls: Ext.baseCSSPrefix + 'grid-cell-selected', - focusedItemCls: Ext.baseCSSPrefix + 'grid-item-focused', - overItemCls: Ext.baseCSSPrefix + 'grid-item-over', - altRowCls: Ext.baseCSSPrefix + 'grid-item-alt', - dirtyCls: Ext.baseCSSPrefix + 'grid-dirty-cell', - rowClsRe: new RegExp('(?:^|\\s*)' + Ext.baseCSSPrefix + 'grid-item-alt(?:\\s+|$)', 'g'), - cellRe: new RegExp(Ext.baseCSSPrefix + 'grid-cell-([^\\s]+)(?:\\s|$)', ''), - positionBody: true, - positionCells: false, - stripeOnUpdate: null, - /** - * @property {Boolean} actionableMode - * This value is `true` when the grid has been set to actionable mode by the user. - * - * See http://www.w3.org/TR/2013/WD-wai-aria-practices-20130307/#grid - * @readonly - */ - actionableMode: false, - // cfg docs inherited - trackOver: true, - /** - * Override this function to apply custom CSS classes to rows during rendering. This function should return the - * CSS class name (or empty string '' for none) that will be added to the row's wrapping element. To apply multiple - * class names, simply return them space-delimited within the string (e.g. 'my-class another-class'). - * Example usage: - * - * viewConfig: { - * getRowClass: function(record, rowIndex, rowParams, store){ - * return record.get("valid") ? "row-valid" : "row-error"; - * } - * } - * - * @param {Ext.data.Model} record The record corresponding to the current row. - * @param {Number} index The row index. - * @param {Object} rowParams **DEPRECATED.** For row body use the - * {@link Ext.grid.feature.RowBody#getAdditionalData getAdditionalData} method of the rowbody feature. - * @param {Ext.data.Store} store The store this grid is bound to - * @return {String} a CSS class name to add to the row. - * @method - */ - getRowClass: null, - /** - * @cfg {Boolean} stripeRows - * True to stripe the rows. - * - * This causes the CSS class **`x-grid-row-alt`** to be added to alternate rows of - * the grid. A default CSS rule is provided which sets a background color, but you can override this - * with a rule which either overrides the **background-color** style using the `!important` - * modifier, or which uses a CSS selector of higher specificity. - */ - stripeRows: true, - /** - * @cfg {Boolean} markDirty - * True to show the dirty cell indicator when a cell has been modified. - */ - markDirty: true, - /** - * @cfg {Boolean} enableTextSelection - * True to enable text selections. - */ - ariaRole: 'rowgroup', - rowAriaRole: 'row', - cellAriaRole: 'gridcell', - /** - * @property {Ext.view.Table} ownerGrid - * A reference to the top-level owning grid component. This is actually the TablePanel - * so it could be a tree. - * @readonly - * @private - * @since 5.0.0 - */ - /** - * @method disable - * Disable this view. - * - * Disables interaction with, and masks this view. - * - * Note that the encapsulating {@link Ext.panel.Table} panel is *not* disabled, and other *docked* - * components such as the panel header, the column header container, and docked toolbars will still be enabled. - * The panel itself can be disabled if that is required, or individual docked components could be disabled. - * - * See {@link Ext.panel.Table #disableColumnHeaders disableColumnHeaders} and {@link Ext.panel.Table #enableColumnHeaders enableColumnHeaders}. - * - * @param {Boolean} [silent=false] Passing `true` will suppress the `disable` event from being fired. - * @since 1.1.0 - */ - /** - * @private - * Outer tpl for TableView just to satisfy the validation within AbstractView.initComponent. - */ - tpl: [ - '{%', - 'view = values.view;', - 'if (!(columns = values.columns)) {', - 'columns = values.columns = view.ownerCt.getVisibleColumnManager().getColumns();', - '}', - 'values.fullWidth = 0;', - // Stamp cellWidth into the columns - 'for (i = 0, len = columns.length; i < len; i++) {', - 'column = columns[i];', - 'values.fullWidth += (column.cellWidth = column.lastBox ? column.lastBox.width : column.width || column.minWidth);', - '}', - // Add the row/column line classes to the container element. - 'tableCls=values.tableCls=[];', - '%}', - '', - // This template is shared on the Ext.view.Table prototype, so we have to - // clean up the closed over variables. Otherwise we'll retain the last values - // of the template execution! - '{% ', - 'view = columns = column = null;', - '%}', - { - definitions: 'var view, tableCls, columns, i, len, column;', - priority: 0 - } - ], - outerRowTpl: [ - '
    ', - // Do NOT emit a tag in case the nextTpl has to emit a column sizer element. - // Browser will create a tbody tag when it encounters the first - '{%', - 'this.nextTpl.applyOut(values, out, parent)', - '%}', - '', - { - priority: 9999 - } - ], - rowTpl: [ - '{%', - 'var dataRowCls = values.recordIndex === -1 ? "" : " ' + Ext.baseCSSPrefix + 'grid-row";', - '%}', - '', - '' + '{%', - 'parent.view.renderCell(values, parent.record, parent.recordIndex, parent.rowIndex, xindex - 1, out, parent)', - '%}', - '', - '', - { - priority: 0 - } - ], - cellTpl: [ - '{tdStyle}"', - ' tabindex="-1" data-columnid="{[values.column.getItemId()]}">', - '
    {style}" ', - '{cellInnerAttr:attributes}>{value}
    ', - '', - { - priority: 0 - } - ], - /** - * @private - * Flag to disable refreshing SelectionModel on view refresh. Table views render rows with selected CSS class already added if necessary. - */ - refreshSelmodelOnRefresh: false, - tableValues: {}, - // Private properties used during the row and cell render process. - // They are allocated here on the prototype, and cleared/re-used to avoid GC churn during repeated rendering. - rowValues: { - itemClasses: [], - rowClasses: [] - }, - cellValues: { - classes: [ - Ext.baseCSSPrefix + 'grid-cell ' + Ext.baseCSSPrefix + 'grid-td' - ] - }, - // for styles shared between cell and rowwrap - /** - * @event beforecellclick - * Fired before the cell click is processed. Return false to cancel the default action. - * @param {Ext.view.Table} this - * @param {HTMLElement} td The TD element for the cell. - * @param {Number} cellIndex - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event cellclick - * Fired when table cell is clicked. - * @param {Ext.view.Table} this - * @param {HTMLElement} td The TD element for the cell. - * @param {Number} cellIndex - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event beforecelldblclick - * Fired before the cell double click is processed. Return false to cancel the default action. - * @param {Ext.view.Table} this - * @param {HTMLElement} td The TD element for the cell. - * @param {Number} cellIndex - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event celldblclick - * Fired when table cell is double clicked. - * @param {Ext.view.Table} this - * @param {HTMLElement} td The TD element for the cell. - * @param {Number} cellIndex - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event beforecellcontextmenu - * Fired before the cell right click is processed. Return false to cancel the default action. - * @param {Ext.view.Table} this - * @param {HTMLElement} td The TD element for the cell. - * @param {Number} cellIndex - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event cellcontextmenu - * Fired when table cell is right clicked. - * @param {Ext.view.Table} this - * @param {HTMLElement} td The TD element for the cell. - * @param {Number} cellIndex - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event beforecellmousedown - * Fired before the cell mouse down is processed. Return false to cancel the default action. - * @param {Ext.view.Table} this - * @param {HTMLElement} td The TD element for the cell. - * @param {Number} cellIndex - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event cellmousedown - * Fired when the mousedown event is captured on the cell. - * @param {Ext.view.Table} this - * @param {HTMLElement} td The TD element for the cell. - * @param {Number} cellIndex - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event beforecellmouseup - * Fired before the cell mouse up is processed. Return false to cancel the default action. - * @param {Ext.view.Table} this - * @param {HTMLElement} td The TD element for the cell. - * @param {Number} cellIndex - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event cellmouseup - * Fired when the mouseup event is captured on the cell. - * @param {Ext.view.Table} this - * @param {HTMLElement} td The TD element for the cell. - * @param {Number} cellIndex - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event beforecellkeydown - * Fired before the cell key down is processed. Return false to cancel the default action. - * @param {Ext.view.Table} this - * @param {HTMLElement} td The TD element for the cell. - * @param {Number} cellIndex - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event cellkeydown - * Fired when the keydown event is captured on the cell. - * @param {Ext.view.Table} this - * @param {HTMLElement} td The TD element for the cell. - * @param {Number} cellIndex - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event rowclick - * Fired when table cell is clicked. - * @param {Ext.view.Table} this - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event rowdblclick - * Fired when table cell is double clicked. - * @param {Ext.view.Table} this - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event rowcontextmenu - * Fired when table cell is right clicked. - * @param {Ext.view.Table} this - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event rowmousedown - * Fired when the mousedown event is captured on the cell. - * @param {Ext.view.Table} this - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event rowmouseup - * Fired when the mouseup event is captured on the cell. - * @param {Ext.view.Table} this - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - /** - * @event rowkeydown - * Fired when the keydown event is captured on the cell. - * @param {Ext.view.Table} this - * @param {Ext.data.Model} record - * @param {HTMLElement} tr The TR element for the cell. - * @param {Number} rowIndex - * @param {Ext.event.Event} e - * @param {Ext.grid.CellContext} e.position A CellContext object which defines the target cell. - */ - constructor: function(config) { - // Adjust our base class if we are inside a TreePanel - if (config.grid.isTree) { - config.baseCls = Ext.baseCSSPrefix + 'tree-view'; - } - this.callParent([ - config - ]); - }, - /** - * @private - * Returns `true` if this view has been configured with variableRowHeight (or this has been set by a plugin/feature) - * which might insert arbitrary markup into a grid item. Or if at least one visible column has been configured - * with variableRowHeight. Or if the store is grouped. - */ - hasVariableRowHeight: function(fromLockingPartner) { - var me = this; - return me.variableRowHeight || me.store.isGrouped() || me.getVisibleColumnManager().hasVariableRowHeight() || // If not already called from a locking partner, and there is a locking partner, - // and the partner has variableRowHeight, then WE have variableRowHeight too. - (!fromLockingPartner && me.lockingPartner && me.lockingPartner.hasVariableRowHeight(true)); - }, - initComponent: function() { - var me = this; - if (me.columnLines) { - me.addCls(me.grid.colLinesCls); - } - if (me.rowLines) { - me.addCls(me.grid.rowLinesCls); - } - /** - * @private - * @property {Ext.dom.Fly} body - * A flyweight Ext.Element which encapsulates a reference to the view's main row containing element. - * *Note that the `dom` reference will not be present until the first data refresh* - */ - me.body = new Ext.dom.Fly(); - me.body.id = me.id + 'gridBody'; - // If trackOver has been turned off, null out the overCls because documented behaviour - // in AbstractView is to turn trackOver on if overItemCls is set. - if (!me.trackOver) { - me.overItemCls = null; - } - me.headerCt.view = me; - // Features need a reference to the grid. - // Grid needs an immediate reference to its view so that the view can reliably be got from the grid during initialization - me.grid.view = me; - me.initFeatures(me.grid); - me.itemSelector = me.getItemSelector(); - me.all = new Ext.view.NodeCache(me); - me.callParent(); - }, - /** - * @private - * Create a config object for this view's selection model based upon the passed grid's configurations. - */ - applySelectionModel: function(selModel, oldSelModel) { - var me = this, - grid = me.ownerGrid, - defaultType = selModel.type, - disableSelection = me.disableSelection || grid.disableSelection; - // If this is the initial configuration, pull overriding configs in from the owning TablePanel. - if (!oldSelModel) { - // Favour a passed instance - if (!(selModel && selModel.isSelectionModel)) { - selModel = grid.selModel || selModel; - } - } - if (selModel) { - if (selModel.isSelectionModel) { - selModel.allowDeselect = grid.allowDeselect || selModel.selectionMode !== 'SINGLE'; - selModel.locked = disableSelection; - } else { - if (typeof selModel === 'string') { - selModel = { - type: selModel - }; - } else // Copy obsolete selType property to type property now that selection models are Factoryable - // TODO: Remove selType config after deprecation period - { - selModel.type = grid.selType || selModel.selType || selModel.type || defaultType; - } - if (!selModel.mode) { - if (grid.simpleSelect) { - selModel.mode = 'SIMPLE'; - } else if (grid.multiSelect) { - selModel.mode = 'MULTI'; - } - } - selModel = Ext.Factory.selection(Ext.apply({ - allowDeselect: grid.allowDeselect, - locked: disableSelection - }, selModel)); - } - } - return selModel; - }, - updateSelectionModel: function(selModel, oldSelModel) { - var me = this; - if (oldSelModel) { - oldSelModel.un({ - scope: me, - lastselectedchanged: me.updateBindSelection, - selectionchange: me.updateBindSelection - }); - Ext.destroy(me.selModelRelayer); - } - me.selModelRelayer = me.relayEvents(selModel, [ - 'selectionchange', - 'beforeselect', - 'beforedeselect', - 'select', - 'deselect', - 'focuschange' - ]); - selModel.on({ - scope: me, - lastselectedchanged: me.updateBindSelection, - selectionchange: me.updateBindSelection - }); - me.selModel = selModel; - }, - getVisibleColumnManager: function() { - return this.ownerCt.getVisibleColumnManager(); - }, - getColumnManager: function() { - return this.ownerCt.getColumnManager(); - }, - getTopLevelVisibleColumnManager: function() { - // ownerGrid refers to the topmost responsible Ext.panel.Grid. - // This could be this view's ownerCt, or if part of a locking arrangement, the locking grid - return this.ownerGrid.getVisibleColumnManager(); - }, - /** - * @private - * Move a grid column from one position to another - * @param {Number} fromIdx The index from which to move columns - * @param {Number} toIdx The index at which to insert columns. - * @param {Number} [colsToMove=1] The number of columns to move beginning at the `fromIdx` - */ - moveColumn: function(fromIdx, toIdx, colsToMove) { - var me = this, - multiMove = colsToMove > 1, - range = multiMove && document.createRange ? document.createRange() : null, - fragment = multiMove && !range ? document.createDocumentFragment() : null, - destinationCellIdx = toIdx, - colCount = me.getGridColumns().length, - lastIndex = colCount - 1, - doFirstLastClasses = (me.firstCls || me.lastCls) && (toIdx === 0 || toIdx === colCount || fromIdx === 0 || fromIdx === lastIndex), - i, j, rows, len, tr, cells, colGroups; - // Dragging between locked and unlocked side first refreshes the view, and calls onHeaderMoved with - // fromIndex and toIndex the same. - if (me.rendered && toIdx !== fromIdx) { - // Grab all rows which have column cells in. - // That is data rows. - rows = me.el.query(me.rowSelector); - for (i = 0 , len = rows.length; i < len; i++) { - tr = rows[i]; - cells = tr.childNodes; - // Keep first cell class and last cell class correct *only if needed* - if (doFirstLastClasses) { - if (cells.length === 1) { - Ext.fly(cells[0]).addCls(me.firstCls); - Ext.fly(cells[0]).addCls(me.lastCls); - - continue; - } - if (fromIdx === 0) { - Ext.fly(cells[0]).removeCls(me.firstCls); - Ext.fly(cells[1]).addCls(me.firstCls); - } else if (fromIdx === lastIndex) { - Ext.fly(cells[lastIndex]).removeCls(me.lastCls); - Ext.fly(cells[lastIndex - 1]).addCls(me.lastCls); - } - if (toIdx === 0) { - Ext.fly(cells[0]).removeCls(me.firstCls); - Ext.fly(cells[fromIdx]).addCls(me.firstCls); - } else if (toIdx === colCount) { - Ext.fly(cells[lastIndex]).removeCls(me.lastCls); - Ext.fly(cells[fromIdx]).addCls(me.lastCls); - } - } - // Move multi using the best technique. - // Extract a range straight into a fragment if possible. - if (multiMove) { - if (range) { - range.setStartBefore(cells[fromIdx]); - range.setEndAfter(cells[fromIdx + colsToMove - 1]); - fragment = range.extractContents(); - } else { - for (j = 0; j < colsToMove; j++) { - fragment.appendChild(cells[fromIdx]); - } - } - tr.insertBefore(fragment, cells[destinationCellIdx] || null); - } else { - tr.insertBefore(cells[fromIdx], cells[destinationCellIdx] || null); - } - } - // Shuffle the elements in all s - colGroups = me.el.query('colgroup'); - for (i = 0 , len = colGroups.length; i < len; i++) { - // Extract the colgroup - tr = colGroups[i]; - // Move multi using the best technique. - // Extract a range straight into a fragment if possible. - if (multiMove) { - if (range) { - range.setStartBefore(tr.childNodes[fromIdx]); - range.setEndAfter(tr.childNodes[fromIdx + colsToMove - 1]); - fragment = range.extractContents(); - } else { - for (j = 0; j < colsToMove; j++) { - fragment.appendChild(tr.childNodes[fromIdx]); - } - } - tr.insertBefore(fragment, tr.childNodes[destinationCellIdx] || null); - } else { - tr.insertBefore(tr.childNodes[fromIdx], tr.childNodes[destinationCellIdx] || null); - } - } - } - }, - // scroll the view to the top - scrollToTop: Ext.emptyFn, - /** - * Add a listener to the main view element. It will be destroyed with the view. - * @private - */ - addElListener: function(eventName, fn, scope) { - this.mon(this, eventName, fn, scope, { - element: 'el' - }); - }, - /** - * Get the leaf columns used for rendering the grid rows. - * @private - */ - getGridColumns: function() { - return this.ownerCt.getVisibleColumnManager().getColumns(); - }, - /** - * Get a leaf level header by index regardless of what the nesting - * structure is. - * @private - * @param {Number} index The index - */ - getHeaderAtIndex: function(index) { - return this.ownerCt.getVisibleColumnManager().getHeaderAtIndex(index); - }, - /** - * Get the cell (td) for a particular record and column. - * @param {Ext.data.Model} record - * @param {Ext.grid.column.Column/Number} column - * @private - */ - getCell: function(record, column) { - var row = this.getRow(record); - if (typeof column === 'number') { - column = this.getHeaderAtIndex(column); - } - return Ext.fly(row).down(column.getCellSelector()); - }, - /** - * Get a reference to a feature - * @param {String} id The id of the feature - * @return {Ext.grid.feature.Feature} The feature. Undefined if not found - */ - getFeature: function(id) { - var features = this.featuresMC; - if (features) { - return features.get(id); - } - }, - /** - * @private - * Finds a features by ftype in the features array - */ - findFeature: function(ftype) { - if (this.features) { - return Ext.Array.findBy(this.features, function(feature) { - if (feature.ftype === ftype) { - return true; - } - }); - } - }, - /** - * Initializes each feature and bind it to this view. - * @private - */ - initFeatures: function(grid) { - var me = this, - i, features, feature, len; - // Row container element emitted by tpl - me.tpl = Ext.XTemplate.getTpl(this, 'tpl'); - // The rowTpl emits a
    - me.rowTpl = Ext.XTemplate.getTpl(this, 'rowTpl'); - me.addRowTpl(Ext.XTemplate.getTpl(this, 'outerRowTpl')); - // Each cell is emitted by the cellTpl - me.cellTpl = Ext.XTemplate.getTpl(this, 'cellTpl'); - me.featuresMC = new Ext.util.MixedCollection(); - features = me.features = me.constructFeatures(); - len = features ? features.length : 0; - for (i = 0; i < len; i++) { - feature = features[i]; - // inject a reference to view and grid - Features need both - feature.view = me; - feature.grid = grid; - me.featuresMC.add(feature); - feature.init(grid); - } - }, - renderTHead: function(values, out, parent) { - var headers = values.view.headerFns, - len, i; - if (headers) { - for (i = 0 , len = headers.length; i < len; ++i) { - headers[i].call(this, values, out, parent); - } - } - }, - // Currently, we don't have ordering support for header/footer functions, - // they will be pushed on at construction time. If the need does arise, - // we can add this functionality in the future, but for now it's not - // really necessary since currently only the summary feature uses this. - addHeaderFn: function(fn) { - var headers = this.headerFns; - if (!headers) { - headers = this.headerFns = []; - } - headers.push(fn); - }, - renderTFoot: function(values, out, parent) { - var footers = values.view.footerFns, - len, i; - if (footers) { - for (i = 0 , len = footers.length; i < len; ++i) { - footers[i].call(this, values, out, parent); - } - } - }, - addFooterFn: function(fn) { - var footers = this.footerFns; - if (!footers) { - footers = this.footerFns = []; - } - footers.push(fn); - }, - addTpl: function(newTpl) { - return this.insertTpl('tpl', newTpl); - }, - addRowTpl: function(newTpl) { - return this.insertTpl('rowTpl', newTpl); - }, - addCellTpl: function(newTpl) { - return this.insertTpl('cellTpl', newTpl); - }, - insertTpl: function(which, newTpl) { - var me = this, - tpl, prevTpl; - // Clone an instantiated XTemplate - if (newTpl.isTemplate) { - newTpl = Ext.Object.chain(newTpl); - } else // If we have been passed an object of the form - // { - // before: fn - // after: fn - // } - // Create a template from it using the object as the member configuration - { - newTpl = new Ext.XTemplate('{%this.nextTpl.applyOut(values, out, parent);%}', newTpl); - } - // Stop at the first TPL who's priority is less than the passed rowTpl - for (tpl = me[which]; newTpl.priority < tpl.priority; tpl = tpl.nextTpl) { - prevTpl = tpl; - } - // If we had skipped over some, link the previous one to the passed rowTpl - if (prevTpl) { - prevTpl.nextTpl = newTpl; - } else // First one - { - me[which] = newTpl; - } - newTpl.nextTpl = tpl; - return newTpl; - }, - tplApplyOut: function(values, out, parent) { - if (this.before) { - if (this.before(values, out, parent) === false) { - return; - } - } - this.nextTpl.applyOut(values, out, parent); - if (this.after) { - this.after(values, out, parent); - } - }, - /** - * @private - * Converts the features array as configured, into an array of instantiated Feature objects. - * - * Must have no side effects other than Feature instantiation. - * - * MUST NOT update the this.features property, and MUST NOT update the instantiated Features. - */ - constructFeatures: function() { - var me = this, - features = me.features, - feature, result, - i = 0, - len; - if (features) { - result = []; - len = features.length; - for (; i < len; i++) { - feature = features[i]; - if (!feature.isFeature) { - feature = Ext.create('feature.' + feature.ftype, feature); - } - result[i] = feature; - } - } - return result; - }, - beforeRender: function() { - this.callParent(); - if (!this.enableTextSelection) { - this.protoEl.unselectable(); - } - }, - getElConfig: function() { - var config = this.callParent(); - // Table views are special in this regard; they should not have - // aria-hidden and aria-disabled attributes. - delete config['aria-hidden']; - delete config['aria-disabled']; - return config; - }, - onBindStore: function(store) { - var me = this, - bufferedRenderer = me.bufferedRenderer; - if (bufferedRenderer && bufferedRenderer.store !== store) { - bufferedRenderer.bindStore(store); - } - // Reset virtual scrolling. - if (me.all && me.all.getCount()) { - if (bufferedRenderer) { - bufferedRenderer.setBodyTop(0); - } - me.clearViewEl(); - } - me.callParent(arguments); - }, - getStoreListeners: function() { - var result = this.callParent(); - // The BufferedRenderer handles clearing down the view on its onStoreClear method - if (this.bufferedRenderer) { - delete result.clear; - } - result.beforepageremove = this.beforePageRemove; - return result; - }, - beforePageRemove: function(pageMap, pageNumber) { - var rows = this.all, - pageSize = pageMap.getPageSize(); - // If the rendered block needs the page, access it which moves it to the end of the LRU cache, and veto removal. - if (rows.startIndex >= (pageNumber - 1) * pageSize && rows.endIndex <= (pageNumber * pageSize - 1)) { - pageMap.get(pageNumber); - return false; - } - }, - /** - * @private - * Template method implemented starting at the AbstractView class. - */ - onViewScroll: function(scroller, x, y) { - // We ignore scrolling caused by focusing - if (!this.ignoreScroll) { - this.callParent([ - scroller, - x, - y - ]); - } - }, - /** - * @private - * Create the DOM element which enapsulates the passed record. - * Used when updating existing rows, so drills down into resulting structure. - */ - createRowElement: function(record, index, updateColumns) { - var me = this, - div = me.renderBuffer, - tplData = me.collectData([ - record - ], index); - tplData.columns = updateColumns; - me.tpl.overwrite(div, tplData); - // We don't want references to be retained on the prototype - me.cleanupData(); - // Return first element within node containing element - return Ext.fly(div).down(me.getNodeContainerSelector(), true).firstChild; - }, - /** - * @private - * Override so that we can use a quicker way to access the row nodes. - * They are simply all child nodes of the nodeContainer element. - */ - bufferRender: function(records, index) { - var me = this, - div = me.renderBuffer, - result, - range = document.createRange ? document.createRange() : null; - me.tpl.overwrite(div, me.collectData(records, index)); - // We don't want references to be retained on the prototype - me.cleanupData(); - // Newly added rows must be untabbable by default - Ext.fly(div).saveTabbableState({ - skipSelf: true, - includeHidden: true - }); - div = Ext.fly(div).down(me.getNodeContainerSelector(), true); - if (range) { - range.selectNodeContents(div); - result = range.extractContents(); - } else { - result = document.createDocumentFragment(); - while (div.firstChild) { - result.appendChild(div.firstChild); - } - } - return { - fragment: result, - children: Ext.Array.toArray(result.childNodes) - }; - }, - collectData: function(records, startIndex) { - var me = this; - me.rowValues.view = me; - me.tableValues.view = me; - me.tableValues.rows = records; - me.tableValues.columns = null; - me.tableValues.viewStartIndex = startIndex; - me.tableValues.touchScroll = me.touchScroll; - me.tableValues.tableStyle = 'width:' + me.headerCt.getTableWidth() + 'px'; - return me.tableValues; - }, - cleanupData: function() { - var tableValues = this.tableValues; - // Clean up references on the prototype - tableValues.view = tableValues.columns = tableValues.rows = this.rowValues.view = null; - }, - /** - * @private. Called when the table changes height. - * For example, see examples/grid/group-summary-grid.html - * If we have flexed column headers, we need to update the header layout - * because it may have to accommodate (or cease to accommodate) a vertical scrollbar. - * Only do this on platforms which have a space-consuming scrollbar. - * Only do it when vertical scrolling is enabled. - */ - refreshSize: function(forceLayout) { - var me = this, - bodySelector = me.getBodySelector(); - // On every update of the layout system due to data update, capture the view's main element in our private flyweight. - // IF there *is* a main element. Some TplFactories emit naked rows. - if (bodySelector) { - // use "down" instead of "child" because the grid table element is not a direct - // child of the view element when a touch scroller is in use. - me.body.attach(me.el.down(bodySelector, true)); - } - if (!me.hasLoadingHeight) { - // Suspend layouts in case the superclass requests a layout. We might too, so they - // must be coalesced. - Ext.suspendLayouts(); - me.callParent(arguments); - // We only need to adjust for height changes in the data if we, or any visible columns have been configured with - // variableRowHeight: true - // OR, if we are being passed the forceUpdate flag which is passed when the view's item count changes. - if (forceLayout || (me.hasVariableRowHeight() && me.dataSource.getCount())) { - me.grid.updateLayout(); - } - Ext.resumeLayouts(true); - } - }, - clearViewEl: function(leaveNodeContainer) { - var me = this, - all = me.all, - store = me.getStore(), - i, nodeContainer, targetEl, - removedItems = all.slice(), - removedRecs = []; - // The purpose of this is to allow boilerplate HTML nodes to remain in place inside a View - // while the transient, templated data can be discarded and recreated. - // - // In particular, this is used in infinite grid scrolling: A very tall "stretcher" element is - // inserted into the View's element to create a scrollbar of the correct proportion. - // - // Also we must ensure that the itemremove event is fired EVERY time an item is removed from the - // view. This is so that widgets rendered into a view by a WidgetColumn can be recycled. - if (me.hasListeners.itemremove) { - for (i = all.startIndex; i <= all.endIndex; i++) { - removedRecs.push(store.getByInternalId(all.item(i, true).getAttribute('data-recordId'))); - } - } - me.fireEvent('itemremove', removedRecs, all.startIndex, removedItems, me); - // AbstractView will clear the view correctly - // It also resets the scrollrange. - me.callParent(); - nodeContainer = Ext.fly(me.getNodeContainer()); - if (nodeContainer && !leaveNodeContainer) { - targetEl = me.getTargetEl(); - if (targetEl.dom !== nodeContainer.dom) { - nodeContainer.destroy(); - } - } - }, - getMaskTarget: function() { - // Masking a TableView masks its IMMEDIATE parent GridPanel's body. - // Disabling/enabling a locking view relays the call to both child views. - return this.ownerCt.body; - }, - statics: { - getBoundView: function(node) { - return Ext.getCmp(node.getAttribute('data-boundView')); - } - }, - getRecord: function(node) { - // If store.destroy has been called before some delayed event fires on a node, we must ignore the event. - if (this.store.destroyed) { - return; - } - if (node.isModel) { - return node; - } - node = this.getNode(node); - // Must use the internalId stamped into the DOM because if this is called after a sort or filter, but - // before the refresh, then the "data-recordIndex" will be stale. - if (node) { - return this.dataSource.getByInternalId(node.getAttribute('data-recordId')); - } - }, - indexOf: function(node) { - node = this.getNode(node); - if (!node && node !== 0) { - return -1; - } - return this.all.indexOf(node); - }, - indexInStore: function(node) { - // We cannot use the stamped in data-recordindex because that is the index in the original configured store - // NOT the index in the dataSource that is being used - that may be a GroupStore. - return node ? this.dataSource.indexOf(this.getRecord(node)) : -1; - }, - indexOfRow: function(record) { - var dataSource = this.dataSource, - idx; - if (record.isCollapsedPlaceholder) { - idx = dataSource.indexOfPlaceholder(record); - } else { - idx = dataSource.indexOf(record); - } - return idx; - }, - renderRows: function(rows, columns, viewStartIndex, out) { - var me = this, - rowValues = me.rowValues, - rowCount = rows.length, - i; - rowValues.view = me; - rowValues.columns = columns; - // The roles are the same for all data rows and cells - rowValues.rowRole = me.rowAriaRole; - me.cellValues.cellRole = me.cellAriaRole; - for (i = 0; i < rowCount; i++ , viewStartIndex++) { - rowValues.itemClasses.length = rowValues.rowClasses.length = 0; - me.renderRow(rows[i], viewStartIndex, out); - } - // Dereference objects since rowValues is a persistent on our prototype - rowValues.view = rowValues.columns = rowValues.record = null; - }, - /* Alternative column sizer element renderer. - renderTHeadColumnSizer: function(values, out) { - var columns = this.getGridColumns(), - len = columns.length, i, - column, width; - - out.push(''); - for (i = 0; i < len; i++) { - column = columns[i]; - width = column.lastBox ? column.lastBox.width : Ext.grid.header.Container.prototype.defaultWidth; - out.push(''); - } - out.push(''); - }, - */ - renderColumnSizer: function(values, out) { - var columns = values.columns || this.getGridColumns(), - len = columns.length, - i, column, width; - out.push(''); - for (i = 0; i < len; i++) { - column = columns[i]; - width = column.cellWidth ? column.cellWidth : Ext.grid.header.Container.prototype.defaultWidth; - out.push(''); - } - out.push(''); - }, - /** - * @private - * Renders the HTML markup string for a single row into the passed array as a sequence of strings, or - * returns the HTML markup for a single row. - * - * @param {Ext.data.Model} record The record to render. - * @param {String[]} [out] A string array onto which to append the resulting HTML string. If omitted, - * the resulting HTML string is returned. - * @return {String} **only when the out parameter is omitted** The resulting HTML string. - */ - renderRow: function(record, rowIdx, out) { - var me = this, - isMetadataRecord = rowIdx === -1, - selModel = me.selectionModel, - rowValues = me.rowValues, - itemClasses = rowValues.itemClasses, - rowClasses = rowValues.rowClasses, - itemCls = me.itemCls, - cls, - rowTpl = me.rowTpl; - // Define the rowAttr object now. We don't want to do it in the treeview treeRowTpl because anything - // this is processed in a deferred callback (such as deferring initial view refresh in gridview) could - // poke rowAttr that are then shared in tableview.rowTpl. See EXTJSIV-9341. - // - // For example, the following shows the shared ref between a treeview's rowTpl nextTpl and the superclass - // tableview.rowTpl: - // - // tree.view.rowTpl.nextTpl === grid.view.rowTpl - // - rowValues.rowAttr = {}; - // Set up mandatory properties on rowValues - rowValues.record = record; - rowValues.recordId = record.internalId; - // recordIndex is index in true store (NOT the data source - possibly a GroupStore) - rowValues.recordIndex = me.store.indexOf(record); - // rowIndex is the row number in the view. - rowValues.rowIndex = rowIdx; - rowValues.rowId = me.getRowId(record); - rowValues.itemCls = rowValues.rowCls = ''; - if (!rowValues.columns) { - rowValues.columns = me.ownerCt.getVisibleColumnManager().getColumns(); - } - itemClasses.length = rowClasses.length = 0; - // If it's a metadata record such as a summary record. - // So do not decorate it with the regular CSS. - // The Feature which renders it must know how to decorate it. - if (!isMetadataRecord) { - itemClasses[0] = itemCls; - if (!me.ownerCt.disableSelection && selModel.isRowSelected) { - // Selection class goes on the outermost row, so it goes into itemClasses - if (selModel.isRowSelected(record)) { - itemClasses.push(me.selectedItemCls); - } - } - if (me.stripeRows && rowIdx % 2 !== 0) { - itemClasses.push(me.altRowCls); - } - if (me.getRowClass) { - cls = me.getRowClass(record, rowIdx, null, me.dataSource); - if (cls) { - rowClasses.push(cls); - } - } - } - if (out) { - rowTpl.applyOut(rowValues, out, me.tableValues); - } else { - return rowTpl.apply(rowValues, me.tableValues); - } - }, - /** - * @private - * Emits the HTML representing a single grid cell into the passed output stream (which is an array of strings). - * - * @param {Ext.grid.column.Column} column The column definition for which to render a cell. - * @param {Number} recordIndex The row index (zero based within the {@link #store}) for which to render the cell. - * @param {Number} rowIndex The row index (zero based within this view for which to render the cell. - * @param {Number} columnIndex The column index (zero based) for which to render the cell. - * @param {String[]} out The output stream into which the HTML strings are appended. - */ - renderCell: function(column, record, recordIndex, rowIndex, columnIndex, out) { - var me = this, - fullIndex, - selModel = me.selectionModel, - cellValues = me.cellValues, - classes = cellValues.classes, - fieldValue = record.data[column.dataIndex], - cellTpl = me.cellTpl, - value, clsInsertPoint, - lastFocused = me.navigationModel.getPosition(); - cellValues.record = record; - cellValues.column = column; - cellValues.recordIndex = recordIndex; - cellValues.rowIndex = rowIndex; - cellValues.columnIndex = cellValues.cellIndex = columnIndex; - cellValues.align = column.align; - cellValues.innerCls = column.innerCls; - cellValues.tdCls = cellValues.tdStyle = cellValues.tdAttr = cellValues.style = ""; - cellValues.unselectableAttr = me.enableTextSelection ? '' : 'unselectable="on"'; - // Begin setup of classes to add to cell - classes[1] = column.getCellId(); - // On IE8, array[len] = 'foo' is twice as fast as array.push('foo') - // So keep an insertion point and use assignment to help IE! - clsInsertPoint = 2; - if (column.renderer && column.renderer.call) { - fullIndex = me.ownerCt.columnManager.getHeaderIndex(column); - value = column.renderer.call(column.usingDefaultRenderer ? column : column.scope || me.ownerCt, fieldValue, cellValues, record, recordIndex, fullIndex, me.dataSource, me); - if (cellValues.css) { - // This warning attribute is used by the compat layer - // TODO: remove when compat layer becomes deprecated - record.cssWarning = true; - cellValues.tdCls += ' ' + cellValues.css; - cellValues.css = null; - } - // Add any tdCls which was added to the cellValues by the renderer. - if (cellValues.tdCls) { - classes[clsInsertPoint++] = cellValues.tdCls; - } - } else { - value = fieldValue; - } - cellValues.value = (value == null || value === '') ? column.emptyCellText : value; - if (column.tdCls) { - classes[clsInsertPoint++] = column.tdCls; - } - if (me.markDirty && record.dirty && record.isModified(column.dataIndex)) { - classes[clsInsertPoint++] = me.dirtyCls; - } - if (column.isFirstVisible) { - classes[clsInsertPoint++] = me.firstCls; - } - if (column.isLastVisible) { - classes[clsInsertPoint++] = me.lastCls; - } - if (!me.enableTextSelection) { - classes[clsInsertPoint++] = me.unselectableCls; - } - if (selModel && (selModel.isCellModel || selModel.isSpreadsheetModel) && selModel.isCellSelected(me, recordIndex, column)) { - classes[clsInsertPoint++] = me.selectedCellCls; - } - if (lastFocused && lastFocused.record.id === record.id && lastFocused.column === column) { - classes[clsInsertPoint++] = me.focusedItemCls; - } - // Chop back array to only what we've set - classes.length = clsInsertPoint; - cellValues.tdCls = classes.join(' '); - cellTpl.applyOut(cellValues, out); - // Dereference objects since cellValues is a persistent var in the XTemplate's scope chain - cellValues.column = cellValues.record = null; - }, - /** - * Returns the table row given the passed Record, or index or node. - * @param {HTMLElement/String/Number/Ext.data.Model} nodeInfo The node or record, or row index. - * to return the top level row. - * @return {HTMLElement} The node or null if it wasn't found - */ - getRow: function(nodeInfo) { - var fly; - if ((!nodeInfo && nodeInfo !== 0) || !this.rendered) { - return null; - } - // An event - if (nodeInfo.target) { - nodeInfo = nodeInfo.target; - } - // An id - if (Ext.isString(nodeInfo)) { - return Ext.fly(nodeInfo).down(this.rowSelector, true); - } - // Row index - if (Ext.isNumber(nodeInfo)) { - fly = this.all.item(nodeInfo); - return fly && fly.down(this.rowSelector, true); - } - // Record - if (nodeInfo.isModel) { - return this.getRowByRecord(nodeInfo); - } - fly = Ext.fly(nodeInfo); - // Passed an item, go down and get the row - if (fly.is(this.itemSelector)) { - return this.getRowFromItem(fly); - } - // Passed a child element of a row - return fly.findParent(this.rowSelector, this.getTargetEl()); - }, - // already an HTMLElement - getRowId: function(record) { - return this.id + '-record-' + record.internalId; - }, - constructRowId: function(internalId) { - return this.id + '-record-' + internalId; - }, - getNodeById: function(id) { - id = this.constructRowId(id); - return this.retrieveNode(id, false); - }, - getRowById: function(id) { - id = this.constructRowId(id); - return this.retrieveNode(id, true); - }, - getNodeByRecord: function(record) { - return this.retrieveNode(this.getRowId(record), false); - }, - getRowByRecord: function(record) { - return this.retrieveNode(this.getRowId(record), true); - }, - getRowFromItem: function(item) { - var rows = Ext.getDom(item).tBodies[0].childNodes, - len = rows.length, - i; - for (i = 0; i < len; i++) { - if (Ext.fly(rows[i]).is(this.rowSelector)) { - return rows[i]; - } - } - }, - retrieveNode: function(id, dataRow) { - var result = this.el.getById(id, true); - if (dataRow && result) { - return Ext.fly(result).down(this.rowSelector, true); - } - return result; - }, - // Links back from grid rows are installed by the XTemplate as data attributes - updateIndexes: Ext.emptyFn, - // Outer table - bodySelector: 'div.' + Ext.baseCSSPrefix + 'grid-item-container', - // Element which contains rows - nodeContainerSelector: 'div.' + Ext.baseCSSPrefix + 'grid-item-container', - // view item. This wraps a data row - itemSelector: 'table.' + Ext.baseCSSPrefix + 'grid-item', - // Grid row which contains cells as opposed to wrapping item. - rowSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-row', - // cell - cellSelector: 'td.' + Ext.baseCSSPrefix + 'grid-cell', - // Select column sizers and cells. - // This may target `` elements as well as `` elements - // `` element is inserted if the first row does not have the regular cell patten (eg is a colspanning group header row) - sizerSelector: '.' + Ext.baseCSSPrefix + 'grid-cell', - innerSelector: 'div.' + Ext.baseCSSPrefix + 'grid-cell-inner', - /** - * Returns a CSS selector which selects the outermost element(s) in this view. - */ - getBodySelector: function() { - return this.bodySelector; - }, - /** - * Returns a CSS selector which selects the element(s) which define the width of a column. - * - * This is used by the {@link Ext.view.TableLayout} when resizing columns. - * - */ - getColumnSizerSelector: function(header) { - var selector = this.sizerSelector + '-' + header.getItemId(); - return 'td' + selector + ',col' + selector; - }, - /** - * Returns a CSS selector which selects items of the view rendered by the outerRowTpl - */ - getItemSelector: function() { - return this.itemSelector; - }, - /** - * Returns a CSS selector which selects a particular column if the desired header is passed, - * or a general cell selector is no parameter is passed. - * - * @param {Ext.grid.column.Column} [header] The column for which to return the selector. If - * omitted, the general cell selector which matches **ant cell** will be returned. - * - */ - getCellSelector: function(header) { - return header ? header.getCellSelector() : this.cellSelector; - }, - /* - * Returns a CSS selector which selects the content carrying element within cells. - */ - getCellInnerSelector: function(header) { - return this.getCellSelector(header) + ' ' + this.innerSelector; - }, - /** - * Adds a CSS Class to a specific row. - * @param {HTMLElement/String/Number/Ext.data.Model} rowInfo An HTMLElement, index or instance of a model - * representing this row - * @param {String} cls - */ - addRowCls: function(rowInfo, cls) { - var row = this.getRow(rowInfo); - if (row) { - Ext.fly(row).addCls(cls); - } - }, - /** - * Removes a CSS Class from a specific row. - * @param {HTMLElement/String/Number/Ext.data.Model} rowInfo An HTMLElement, index or instance of a model - * representing this row - * @param {String} cls - */ - removeRowCls: function(rowInfo, cls) { - var row = this.getRow(rowInfo); - if (row) { - Ext.fly(row).removeCls(cls); - } - }, - // GridSelectionModel invokes onRowSelect as selection changes - onRowSelect: function(rowIdx) { - var me = this, - rowNode; - me.addItemCls(rowIdx, me.selectedItemCls); - rowNode = me.getRow(rowIdx); - if (rowNode) { - rowNode.setAttribute('aria-selected', true); - } - if (Ext.isIE8) { - me.repaintBorder(rowIdx + 1); - } - }, - // GridSelectionModel invokes onRowDeselect as selection changes - onRowDeselect: function(rowIdx) { - var me = this, - rowNode; - me.removeItemCls(rowIdx, me.selectedItemCls); - rowNode = me.getRow(rowIdx); - if (rowNode) { - rowNode.removeAttribute('aria-selected'); - } - if (Ext.isIE8) { - me.repaintBorder(rowIdx + 1); - } - }, - onCellSelect: function(position) { - var cell = this.getCellByPosition(position); - if (cell) { - cell.addCls(this.selectedCellCls); - cell.dom.setAttribute('aria-selected', true); - } - }, - onCellDeselect: function(position) { - var cell = this.getCellByPosition(position, true); - if (cell) { - Ext.fly(cell).removeCls(this.selectedCellCls); - cell.removeAttribute('aria-selected'); - } - }, - // Old API. Used by tests now to test coercion of navigation from hidden column to closest visible. - // Position.column includes all columns including hidden ones. - getCellInclusive: function(position, returnDom) { - if (position) { - var row = this.getRow(position.row), - header = this.ownerCt.getColumnManager().getHeaderAtIndex(position.column); - if (header && row) { - return Ext.fly(row).down(this.getCellSelector(header), returnDom); - } - } - return false; - }, - getCellByPosition: function(position, returnDom) { - if (position) { - var view = position.view || this, - row = view.getRow(position.record || position.row), - header = position.column.isColumn ? position.column : view.getVisibleColumnManager().getHeaderAtIndex(position.column); - if (header && row) { - return Ext.fly(row).down(view.getCellSelector(header), returnDom); - } - } - return false; - }, - onFocusEnter: function(e) { - var me = this, - fromComponent = e.fromComponent, - navigationModel = me.getNavigationModel(), - focusPosition, - br = me.bufferedRenderer, - focusRecord, focusRowIdx, focusTarget, scroller; - // Focusing an internal focusable while TD navigation is disabled; - // We do not intervene. - if (me.actionableMode) { - return; - } - // The underlying DOM event - e = e.event; - // We can only focus if there are rows in the row cache to focus *and* records - // in the store to back them. Buffered Stores can produce a state where - // the view is not cleared on the leading end of a reload operation, but the - // store can be empty. - if (!me.cellFocused && me.all.getCount() && me.dataSource.getCount()) { - focusTarget = e.getTarget(); - // Programmatic focus of a cell... - if (Ext.fly(focusTarget).is(me.getCellSelector())) { - focusPosition = new Ext.grid.CellContext(me).setPosition(me.getRecord(focusTarget), me.getHeaderByCell(focusTarget)); - } - // If what is being focused an interior element, but is not a cell, allow it to proceed. - // This means that we must be in actionable mode. This will happen when an ActionColumn invokes a modal window - // and that window is dismissed leading to automatic focus of the previously focused element. - // Testing whether the focusTarget isFocusable is a fix for IE. It can sometimes fire a focus event with the .x-scroll-scroller as the target - else if (focusTarget && Ext.fly(focusTarget).isFocusable() && me.el.contains(focusTarget) && focusTarget !== me.el.dom) { - me.ownerGrid.setActionableMode(true, new Ext.grid.CellContext(me).setPosition(me.getRecord(focusTarget), me.getHeaderByCell(Ext.fly(focusTarget).up(me.getCellSelector())))); - // setActionableMode focuses the *first* tabbable element in the cell. - // If focus if entering into another element (eg multiple action icons in an ActionColumn), then redirect it. - Ext.fly(focusTarget).focus(); - } else // The View's el has been focused. - // We now have to decide which cell to focus - { - focusPosition = me.lastFocused; - // Default to the first cell if the NavigationModel has never focused anything - if (focusPosition) { - scroller = me.getScrollable(); - if (!scroller || scroller.isInView(focusPosition.getRow()).y) { - focusRecord = focusPosition.record; - } - } else { - focusPosition = new Ext.grid.CellContext((me.isNormalView && me.lockingPartner.grid.isVisible()) ? me.lockingPartner : me).setColumn(0); - } - // Tabbing in from one of our column headers; the user will expect to move into that column - if (fromComponent && fromComponent.isColumn && fromComponent.getView() === me) { - focusPosition.view = me; - focusPosition.setColumn(fromComponent); - } - // If we do not have a visible lastFocused, find the first visible one - if (!focusRecord) { - // Focus the first visible row - focusRowIdx = br ? br.getFirstVisibleRowIndex() : 0; - focusRecord = me.dataSource.getAt(focusRowIdx); - // Skip over non-row producing records like collapsed placeholders. - // We cannot focus these yet. - while (focusRecord && focusRecord.isNonData) { - focusRowIdx++; - focusRecord = me.dataSource.getAt(focusRowIdx); - } - if (focusRecord) { - focusPosition.setRow(focusRecord); - } else { - focusPosition = null; - } - } - // Not a descendant which we allow to carry focus. Focus the view el. - if (!focusPosition) { - e.stopEvent(); - me.el.focus(); - return; - } - } - } - // We calculated a cell to focus on. Either from the target element, or the last focused position - if (focusPosition) { - navigationModel.setPosition(focusPosition, null, e, null, true); - // We now contain focus if that was successful - me.cellFocused = !!navigationModel.getPosition(); - if (me.cellFocused) { - me.el.dom.setAttribute('tabIndex', '-1'); - // Disable tabbability of elements within this view. - me.toggleChildrenTabbability(false); - } - } - // Skip the AbstractView's implementation. - // It initializes its NavModel differently. - Ext.Component.prototype.onFocusEnter.call(me, e); - }, - onFocusLeave: function(e) { - var me = this, - // See if focus is really leaving the grid. - // If we have a locking partner, and focus is going to that, we're NOT leaving the grid. - isLeavingGrid = !me.lockingPartner || !e.toComponent || (e.toComponent !== me.lockingPartner && !me.lockingPartner.isAncestor(e.toComponent)); - // If the blur was caused by a refresh, we expect things to be refocused. - if (!me.refreshing) { - // Ignore this event if we do not actually contain focus. - // CellEditors are rendered into the view's encapculating element, - // So focusleave will fire when they are programatically blurred. - // We will not have focus at that point. - if (me.cellFocused) { - // Blur the focused cell unless we are navigating into a locking partner, - // in which case, the focus of that will setPosition to the target - // without an intervening position to null. - if (isLeavingGrid) { - me.getNavigationModel().setPosition(null, null, e.event, null, true); - } - me.cellFocused = false; - me.focusEl = me.el; - me.focusEl.dom.setAttribute('tabIndex', 0); - } - // Exiting to outside, switch back to navigation mode before clearing the navigation position - // so that the current position's row can have its tabbability saved. - if (isLeavingGrid) { - if (me.ownerGrid.actionableMode) { - me.ownerGrid.setActionableMode(false); - } - } - // Skip the AbstractView's implementation. - Ext.Component.prototype.onFocusLeave.call(me, e); - } - }, - // GridSelectionModel invokes onRowFocus to 'highlight' - // the last row focused - onRowFocus: function(rowIdx, highlight, supressFocus) { - var me = this; - if (highlight) { - me.addItemCls(rowIdx, me.focusedItemCls); - if (!supressFocus) { - me.focusRow(rowIdx); - } - } else //this.el.dom.setAttribute('aria-activedescendant', row.id); - { - me.removeItemCls(rowIdx, me.focusedItemCls); - } - if (Ext.isIE8) { - me.repaintBorder(rowIdx + 1); - } - }, - /** - * Focuses a particular row and brings it into view. Will fire the rowfocus event. - * @param {HTMLElement/String/Number/Ext.data.Model} row An HTMLElement template node, index of a template node, the id of a template node or the - * @param {Boolean/Number} [delay] Delay the focus this number of milliseconds (true for 10 milliseconds). - * record associated with the node. - */ - focusRow: function(row, delay) { - var me = this, - focusTask = me.getFocusTask(); - if (delay) { - focusTask.delay(Ext.isNumber(delay) ? delay : 10, me.focusRow, me, [ - row, - false - ]); - return; - } - // An immediate focus call must cancel any outstanding delayed focus calls. - focusTask.cancel(); - // Do not attempt to focus if hidden or within collapsed Panel. - if (me.isVisible(true)) { - me.getNavigationModel().setPosition(me.getRecord(row)); - } - }, - // Override the version in Ext.view.View because the focusable elements are the grid cells. - /** - * @override Ext.view.View - * Focuses a particular row and brings it into view. Will fire the rowfocus event. - * @param {HTMLElement/String/Number/Ext.data.Model} row An HTMLElement template node, index of a template node, the id of a template node or the - * @param {Boolean/Number} [delay] Delay the focus this number of milliseconds (true for 10 milliseconds). - * record associated with the node. - */ - focusNode: function(row, delay) { - this.focusRow(row, delay); - }, - scrollRowIntoView: function(row, animate) { - row = this.getRow(row); - if (row) { - this.scrollElIntoView(row, false, animate); - } - }, - /** - * Focuses a particular cell and brings it into view. Will fire the rowfocus event. - * @param {Ext.grid.CellContext} pos The cell to select - * @param {Boolean/Number} [delay] Delay the focus this number of milliseconds (true for 10 milliseconds). - */ - focusCell: function(position, delay) { - var me = this, - cell, - focusTask = me.getFocusTask(); - if (delay) { - focusTask.delay(Ext.isNumber(delay) ? delay : 10, me.focusCell, me, [ - position, - false - ]); - return; - } - // An immediate focus call must cancel any outstanding delayed focus calls. - focusTask.cancel(); - // Do not attempt to focus if hidden or within collapsed Panel - // Maintainer: Note that to avoid an unnecessary call to me.getCellByPosition if not visible, or another, nested if test, - // the assignment of the cell var is embedded inside the condition expression. - if (me.isVisible(true) && (cell = me.getCellByPosition(position))) { - me.getNavigationModel().setPosition(position); - } - }, - getLastFocused: function() { - var me = this, - lastFocused = me.lastFocused; - if (lastFocused && lastFocused.record && lastFocused.column) { - // If the last focused record or column has gone away, or the record is no longer in the visible rendered block, we have no lastFocused - if (me.dataSource.indexOf(lastFocused.record) !== -1 && me.getVisibleColumnManager().indexOf(lastFocused.column) !== -1 && me.getNode(lastFocused.record)) { - return lastFocused; - } - } - }, - scrollCellIntoView: function(cell, animate) { - if (cell.isCellContext) { - cell = this.getCellByPosition(cell); - } - if (cell) { - this.scrollElIntoView(cell, null, animate); - } - }, - scrollElIntoView: function(el, hscroll, animate) { - var scroller = this.getScrollable(); - if (scroller) { - scroller.scrollIntoView(el, hscroll, animate); - } - }, - syncRowHeightBegin: function() { - var me = this, - itemEls = me.all, - ln = itemEls.count, - synchronizer = [], - RowSynchronizer = Ext.grid.locking.RowSynchronizer, - i, j, rowSync; - for (i = 0 , j = itemEls.startIndex; i < ln; i++ , j++) { - synchronizer[i] = rowSync = new RowSynchronizer(me, itemEls.elements[j]); - rowSync.reset(); - } - return synchronizer; - }, - syncRowHeightClear: function(synchronizer) { - var me = this, - itemEls = me.all, - ln = itemEls.count, - i; - for (i = 0; i < ln; i++) { - synchronizer[i].reset(); - } - }, - syncRowHeightMeasure: function(synchronizer) { - var ln = synchronizer.length, - i; - for (i = 0; i < ln; i++) { - synchronizer[i].measure(); - } - }, - syncRowHeightFinish: function(synchronizer, otherSynchronizer) { - var ln = synchronizer.length, - bufferedRenderer = this.bufferedRenderer, - i; - for (i = 0; i < ln; i++) { - synchronizer[i].finish(otherSynchronizer[i]); - } - // Ensure that both BufferedRenderers have the same idea about scroll range and row height - if (bufferedRenderer) { - bufferedRenderer.syncRowHeightsFinish(); - } - }, - handleUpdate: function(store, record, operation, changedFieldNames) { - operation = operation || Ext.data.Model.EDIT; - var me = this, - recordIndex = me.store.indexOf(record), - rowTpl = me.rowTpl, - markDirty = me.markDirty, - dirtyCls = me.dirtyCls, - clearDirty = operation !== Ext.data.Model.EDIT, - columnsToUpdate = [], - hasVariableRowHeight = me.variableRowHeight, - updateTypeFlags = 0, - ownerCt = me.ownerCt, - cellFly = me.cellFly || (me.self.prototype.cellFly = new Ext.dom.Fly()), - oldItemDom, oldDataRow, newItemDom, newAttrs, attLen, attName, attrIndex, overItemCls, columns, column, len, i, cellUpdateFlag, cell, fieldName, value, defaultRenderer, scope, elData, emptyValue; - if (me.viewReady) { - // Table row being updated - oldItemDom = me.getNodeByRecord(record); - // Row might not be rendered due to buffered rendering or being part of a collapsed group... - if (oldItemDom) { - // refreshNode can be called on a collapsed placeholder record. - // Update it from a new rendering. - if (record.isCollapsedPlaceholder) { - Ext.fly(oldItemDom).syncContent(me.createRowElement(record, me.indexOfRow(record))); - return; - } - overItemCls = me.overItemCls; - columns = me.ownerCt.getVisibleColumnManager().getColumns(); - // Collect an array of the columns which must be updated. - // If the field at this column index was changed, or column has a custom renderer - // (which means value could rely on any other changed field) we include the column. - for (i = 0 , len = columns.length; i < len; i++) { - column = columns[i]; - // We are not going to update the cell, but we still need to mark it as dirty. - if (column.preventUpdate) { - cell = Ext.fly(oldItemDom).down(column.getCellSelector(), true); - // Mark the field's dirty status if we are configured to do so (defaults to true) - if (cell && !clearDirty && markDirty) { - cellFly.attach(cell); - if (record.isModified(column.dataIndex)) { - cellFly.addCls(dirtyCls); - } else { - cellFly.removeCls(dirtyCls); - } - } - } else { - // 0 = Column doesn't need update. - // 1 = Column needs update, and renderer has > 1 argument; We need to render a whole new HTML item. - // 2 = Column needs update, but renderer has 1 argument or column uses an updater. - cellUpdateFlag = me.shouldUpdateCell(record, column, changedFieldNames); - if (cellUpdateFlag) { - // Track if any of the updating columns yields a flag with the 1 bit set. - // This means that there is a custom renderer involved and a new TableView item - // will need rendering. - updateTypeFlags = updateTypeFlags | cellUpdateFlag; - // jshint ignore:line - columnsToUpdate[columnsToUpdate.length] = column; - hasVariableRowHeight = hasVariableRowHeight || column.variableRowHeight; - } - } - } - // Give CellEditors or other transient in-cell items a chance to get out of the way - // if there are in the cells destined for update. - me.fireEvent('beforeitemupdate', record, recordIndex, oldItemDom, columnsToUpdate); - // If there's no data row (some other rowTpl has been used; eg group header) - // or we have a getRowClass - // or one or more columns has a custom renderer - // or there's more than one , we must use the full render pathway to create a whole new TableView item - if (me.getRowClass || !me.getRowFromItem(oldItemDom) || (updateTypeFlags & 1) || // jshint ignore:line - (oldItemDom.tBodies[0].childNodes.length > 1)) { - elData = oldItemDom._extData; - newItemDom = me.createRowElement(record, me.indexOfRow(record), columnsToUpdate); - if (Ext.fly(oldItemDom, '_internal').hasCls(overItemCls)) { - Ext.fly(newItemDom).addCls(overItemCls); - } - // Copy new row attributes across. Use IE-specific method if possible. - // In IE10, there is a problem where the className will not get updated - // in the view, even though the className on the dom element is correct. - // See EXTJSIV-9462 - if (Ext.isIE9m && oldItemDom.mergeAttributes) { - oldItemDom.mergeAttributes(newItemDom, true); - } else { - newAttrs = newItemDom.attributes; - attLen = newAttrs.length; - for (attrIndex = 0; attrIndex < attLen; attrIndex++) { - attName = newAttrs[attrIndex].name; - if (attName !== 'id') { - oldItemDom.setAttribute(attName, newAttrs[attrIndex].value); - } - } - } - // The element's data is no longer synchronized. We just overwrite it in the DOM - if (elData) { - elData.isSynchronized = false; - } - // If we have columns which may *need* updating (think locked side of lockable grid with all columns unlocked) - // and the changed record is within our view, then update the view. - if (columns.length && (oldDataRow = me.getRow(oldItemDom))) { - me.updateColumns(oldDataRow, Ext.fly(newItemDom).down(me.rowSelector, true), columnsToUpdate); - } - // Loop thru all of rowTpls asking them to sync the content they are responsible for if any. - while (rowTpl) { - if (rowTpl.syncContent) { - // *IF* we are selectively updating columns (have been passed changedFieldNames), then pass the column set, else - // pass null, and it will sync all content. - if (rowTpl.syncContent(oldItemDom, newItemDom, changedFieldNames ? columnsToUpdate : null) === false) { - break; - } - } - rowTpl = rowTpl.nextTpl; - } - } else // No custom renderers found in columns to be updated, we can simply update the existing cells. - { - // Loop through columns which need updating. - for (i = 0 , len = columnsToUpdate.length; i < len; i++) { - column = columnsToUpdate[i]; - // The dataIndex of the column is the field name - fieldName = column.dataIndex; - value = record.get(fieldName); - cell = Ext.fly(oldItemDom).down(column.getCellSelector(), true); - cellFly.attach(cell); - // Mark the field's dirty status if we are configured to do so (defaults to true) - if (!clearDirty && markDirty) { - if (record.isModified(column.dataIndex)) { - cellFly.addCls(dirtyCls); - } else { - cellFly.removeCls(dirtyCls); - } - } - defaultRenderer = column.usingDefaultRenderer; - scope = defaultRenderer ? column : column.scope; - // Call the column updater which gets passed the TD element - if (column.updater) { - Ext.callback(column.updater, scope, [ - cell, - value, - record, - me, - me.dataSource - ], 0, column, ownerCt); - } else { - if (column.renderer) { - value = Ext.callback(column.renderer, scope, [ - value, - null, - record, - 0, - 0, - me.dataSource, - me - ], 0, column, ownerCt); - } - emptyValue = value == null || value === ''; - value = emptyValue ? column.emptyCellText : value; - // Update the value of the cell's inner in the best way. - // We only use innerHTML of the cell's inner DIV if the renderer produces HTML - // Otherwise we change the value of the single text node within the inner DIV - // The emptyValue may be HTML, typically defaults to   - if (column.producesHTML || emptyValue) { - cellFly.down(me.innerSelector, true).innerHTML = value; - } else { - cellFly.down(me.innerSelector, true).childNodes[0].data = value; - } - } - // Add the highlight class if there is one - if (me.highlightClass) { - Ext.fly(cell).addCls(me.highlightClass); - // Start up a DelayedTask which will purge the changedCells stack, removing the highlight class - // after the expiration time - if (!me.changedCells) { - me.self.prototype.changedCells = []; - me.prototype.clearChangedTask = new Ext.util.DelayedTask(me.clearChangedCells, me.prototype); - me.clearChangedTask.delay(me.unhighlightDelay); - } - // Post a changed cell to the stack along with expiration time - me.changedCells.push({ - cell: cell, - cls: me.highlightClass, - expires: Ext.Date.now() + 1000 - }); - } - } - } - // If we have a commit or a reject, some fields may no longer be dirty but may - // not appear in the modified field names. Remove all the dirty class here to be sure. - if (clearDirty && markDirty && !record.dirty) { - Ext.fly(oldItemDom, '_internal').select('.' + dirtyCls).removeCls(dirtyCls); - } - // Coalesce any layouts which happen due to any itemupdate handlers (eg Widget columns) with the final refreshSize layout. - if (hasVariableRowHeight) { - Ext.suspendLayouts(); - } - // Since we don't actually replace the row, we need to fire the event with the old row - // because it's the thing that is still in the DOM - me.fireEvent('itemupdate', record, recordIndex, oldItemDom); - // We only need to update the layout if any of the columns can change the row height. - if (hasVariableRowHeight) { - // Must climb to ownerGrid in case we've only updated one field in one side of a lockable assembly. - // ownerGrid is always the topmost GridPanel. - me.ownerGrid.updateLayout(); - // Ensure any layouts queued by itemupdate handlers and/or the refreshSize call are executed. - Ext.resumeLayouts(true); - } - } - } - }, - afterComponentLayout: function() { - var scroller; - this.callParent(arguments); - // BufferedRenderer gets a pair of calls bracketting a component layout - // which enables it to calsulate its scroll range and update the scroller. - // If no BufferedRenderer, we do it here. - if (this.touchScroll && !this.bufferedRenderer && (scroller = this.getScrollable())) { - scroller.refresh(); - } - }, - clearChangedCells: function() { - var me = this, - now = Ext.Date.now(), - changedCell; - for (var i = 0, - len = me.changedCells.length; i < len; ) { - changedCell = me.changedCells[i]; - if (changedCell.expires <= now) { - Ext.fly(changedCell.cell).removeCls(changedCell.highlightClass); - Ext.Array.erase(me.changedCells, i, 1); - len--; - } else { - break; - } - } - // Keep repeating the delay until all highlighted cells have been cleared - if (len) { - me.clearChangedTask.delay(me.unhighlightDelay); - } - }, - updateColumns: function(oldRowDom, newRowDom, columnsToUpdate) { - var me = this, - newAttrs, attLen, attName, attrIndex, - colCount = columnsToUpdate.length, - colIndex, column, oldCell, newCell, - cellSelector = me.getCellSelector(); - // Copy new row attributes across. Use IE-specific method if possible. - // Must do again at this level because the row DOM passed here may be the nested row in a row wrap. - if (oldRowDom.mergeAttributes) { - oldRowDom.mergeAttributes(newRowDom, true); - } else { - newAttrs = newRowDom.attributes; - attLen = newAttrs.length; - for (attrIndex = 0; attrIndex < attLen; attrIndex++) { - attName = newAttrs[attrIndex].name; - if (attName !== 'id') { - oldRowDom.setAttribute(attName, newAttrs[attrIndex].value); - } - } - } - // Replace changed cells in the existing row structure with the new version from the rendered row. - for (colIndex = 0; colIndex < colCount; colIndex++) { - column = columnsToUpdate[colIndex]; - // Pluck out cells using the column's unique cell selector. - // Becuse in a wrapped row, there may be several TD elements. - cellSelector = me.getCellSelector(column); - oldCell = Ext.fly(oldRowDom).selectNode(cellSelector); - newCell = Ext.fly(newRowDom).selectNode(cellSelector); - // Carefully replace just the *contents* of the cell. - Ext.fly(oldCell).syncContent(newCell); - } - }, - /** - * @private - * Decides whether the column needs updating - * @return {Number} 0 = Doesn't need update. - * 1 = Column needs update, and renderer has > 1 argument; We need to render a whole new HTML item. - * 2 = Column needs update, but renderer has 1 argument or column uses an updater. - */ - shouldUpdateCell: function(record, column, changedFieldNames) { - return column.shouldUpdateCell(record, changedFieldNames); - }, - /** - * Refreshes the grid view. Sets the sort state and focuses the previously focused row. - */ - refresh: function() { - var me = this, - scroller, - restoreFocus = me.saveFocusState(); - if (me.destroying) { - return; - } - me.callParent(arguments); - // If focus was in any way in this view, this will restore it - restoreFocus(); - me.headerCt.setSortState(); - // Create horizontal stretcher element if no records in view and there is overflow of the header container. - // Element will be transient and destroyed by the next refresh. - if (me.touchScroll && me.el && !me.all.getCount() && me.headerCt && me.headerCt.tooNarrow) { - scroller = me.getScrollable(); - if (scroller && scroller.isTouchScroller) { - scroller.setSize({ - x: me.headerCt.getTableWidth(), - y: scroller.getSize().y - }); - } - } - }, - processContainerEvent: function(e) { - // If we find a component & it belongs to our grid, don't fire the event. - // For example, grid editors resolve to the parent grid - var cmp = Ext.Component.fromElement(e.target.parentNode); - if (cmp && cmp.up(this.ownerCt)) { - return false; - } - }, - processItemEvent: function(record, item, rowIndex, e) { - var me = this, - self = me.self, - map = self.EventMap, - type = e.type, - features = me.features, - len = features.length, - i, cellIndex, result, feature, column, - eventPosition = e.position = me.eventPosition || (me.eventPosition = new Ext.grid.CellContext()), - row, cell; - // IE has a bug whereby if you mousedown in a cell editor in one side of a locking grid and then - // drag out of that, and mouseup in *the other side*, the mousedowned side still receives the event! - // Even though the mouseup target is *not* within it! Ignore the mouseup in this case. - if (Ext.isIE && type === 'mouseup' && !e.within(me.el)) { - return false; - } - // Only process the event if it occurred within an item which maps to a record in the store - if (me.indexInStore(item) !== -1) { - row = eventPosition.rowElement = Ext.fly(item).down(me.rowSelector, true); - // Access the cell from the event target. - cell = e.getTarget(me.getCellSelector(), row); - type = self.TouchEventMap[type] || type; - if (cell) { - if (!cell.parentNode) { - // If we have no parentNode, the td has been removed from the DOM, probably via an update, - // so just jump out since the target for the event isn't valid - return false; - } - column = me.getHeaderByCell(cell); - // Find the index of the header in the *full* (including hidden columns) leaf column set. - // Because In 4.0.0 we rendered hidden cells, and the cellIndex included the hidden ones. - cellIndex = me.ownerCt.getColumnManager().getHeaderIndex(column); - } else { - cellIndex = -1; - } - eventPosition.setAll(me, rowIndex, column ? me.getVisibleColumnManager().getHeaderIndex(column) : -1, record, column); - eventPosition.cellElement = cell; - result = me.fireEvent('uievent', type, me, cell, rowIndex, cellIndex, e, record, row); - // If the event has been stopped by a handler, tell the selModel (if it is interested) and return early. - // For example, action columns by default will stop event propagation by returning `false` from its - // 'uievent' event handler. - if ((result === false || me.callParent(arguments) === false)) { - return false; - } - for (i = 0; i < len; ++i) { - feature = features[i]; - // In some features, the first/last row might be wrapped to contain extra info, - // such as grouping or summary, so we may need to stop the event - if (feature.wrapsItem) { - if (feature.vetoEvent(record, row, rowIndex, e) === false) { - // If the feature is vetoing the event, there's a good chance that - // it's for some feature action in the wrapped row. - me.processSpecialEvent(e); - return false; - } - } - } - // if the element whose event is being processed is not an actual cell (for example if using a rowbody - // feature and the rowbody element's event is being processed) then do not fire any "cell" events - // Don't handle cellmouseenter and cellmouseleave events for now - if (cell && type !== 'mouseover' && type !== 'mouseout') { - result = !// We are adding cell and feature events - ((me['onBeforeCell' + map[type]](cell, cellIndex, record, row, rowIndex, e) === false) || (me.fireEvent('beforecell' + type, me, cell, cellIndex, record, row, rowIndex, e) === false) || (me['onCell' + map[type]](cell, cellIndex, record, row, rowIndex, e) === false) || (me.fireEvent('cell' + type, me, cell, cellIndex, record, row, rowIndex, e) === false)); - } - if (result !== false) { - result = me.fireEvent('row' + type, me, record, row, rowIndex, e); - } - return result; - } else { - // If it's not in the store, it could be a feature event, so check here - this.processSpecialEvent(e); - // Prevent focus/selection here until proper focus handling is added for non-data rows - // This should probably be removed once this is implemented. - e.preventDefault(); - return false; - } - }, - processSpecialEvent: function(e) { - var me = this, - features = me.features, - ln = features.length, - type = e.type, - i, feature, prefix, featureTarget, beforeArgs, args, - panel = me.ownerCt; - me.callParent(arguments); - if (type === 'mouseover' || type === 'mouseout') { - return; - } - type = me.self.TouchEventMap[type] || type; - for (i = 0; i < ln; i++) { - feature = features[i]; - if (feature.hasFeatureEvent) { - featureTarget = e.getTarget(feature.eventSelector, me.getTargetEl()); - if (featureTarget) { - prefix = feature.eventPrefix; - // allows features to implement getFireEventArgs to change the - // fireEvent signature - beforeArgs = feature.getFireEventArgs('before' + prefix + type, me, featureTarget, e); - args = feature.getFireEventArgs(prefix + type, me, featureTarget, e); - if (// before view event - (me.fireEvent.apply(me, beforeArgs) === false) || // panel grid event - (panel.fireEvent.apply(panel, beforeArgs) === false) || // view event - (me.fireEvent.apply(me, args) === false) || // panel event - (panel.fireEvent.apply(panel, args) === false)) { - return false; - } - } - } - } - return true; - }, - onCellMouseDown: Ext.emptyFn, - onCellLongPress: Ext.emptyFn, - onCellMouseUp: Ext.emptyFn, - onCellClick: Ext.emptyFn, - onCellDblClick: Ext.emptyFn, - onCellContextMenu: Ext.emptyFn, - onCellKeyDown: Ext.emptyFn, - onCellKeyUp: Ext.emptyFn, - onCellKeyPress: Ext.emptyFn, - onBeforeCellMouseDown: Ext.emptyFn, - onBeforeCellLongPress: Ext.emptyFn, - onBeforeCellMouseUp: Ext.emptyFn, - onBeforeCellClick: Ext.emptyFn, - onBeforeCellDblClick: Ext.emptyFn, - onBeforeCellContextMenu: Ext.emptyFn, - onBeforeCellKeyDown: Ext.emptyFn, - onBeforeCellKeyUp: Ext.emptyFn, - onBeforeCellKeyPress: Ext.emptyFn, - /** - * Expands a particular header to fit the max content width. - * @deprecated Use {@link #autoSizeColumn} instead. - */ - expandToFit: function(header) { - this.autoSizeColumn(header); - }, - /** - * Sizes the passed header to fit the max content width. - * *Note that group columns shrinkwrap around the size of leaf columns. Auto sizing a group column - * autosizes descendant leaf columns.* - * @param {Ext.grid.column.Column/Number} header The header (or index of header) to auto size. - */ - autoSizeColumn: function(header) { - if (Ext.isNumber(header)) { - header = this.getGridColumns()[header]; - } - if (header) { - if (header.isGroupHeader) { - header.autoSize(); - return; - } - delete header.flex; - header.setWidth(this.getMaxContentWidth(header)); - } - }, - /** - * Returns the max contentWidth of the header's text and all cells - * in the grid under this header. - * @private - */ - getMaxContentWidth: function(header) { - var me = this, - cells = me.el.query(header.getCellInnerSelector()), - originalWidth = header.getWidth(), - i = 0, - ln = cells.length, - columnSizer = me.body.select(me.getColumnSizerSelector(header)), - max = Math.max, - widthAdjust = 0, - maxWidth; - if (ln > 0) { - if (Ext.supports.ScrollWidthInlinePaddingBug) { - widthAdjust += me.getCellPaddingAfter(cells[0]); - } - if (me.columnLines) { - widthAdjust += Ext.fly(cells[0].parentNode).getBorderWidth('lr'); - } - } - // Set column width to 1px so we can detect the content width by measuring scrollWidth - columnSizer.setWidth(1); - // We are about to measure the offsetWidth of the textEl to determine how much - // space the text occupies, but it will not report the correct width if the titleEl - // has text-overflow:ellipsis. Set text-overflow to 'clip' before proceeding to - // ensure we get the correct measurement. - header.textEl.setStyle({ - "text-overflow": 'clip', - display: 'table-cell' - }); - // Allow for padding round text of header - maxWidth = header.textEl.dom.offsetWidth + header.titleEl.getPadding('lr'); - // revert to using text-overflow defined by the stylesheet - header.textEl.setStyle({ - "text-overflow": '', - display: '' - }); - for (; i < ln; i++) { - maxWidth = max(maxWidth, cells[i].scrollWidth); - } - // in some browsers, the "after" padding is not accounted for in the scrollWidth - maxWidth += widthAdjust; - // 40 is the minimum column width. TODO: should this be configurable? - // One extra pixel needed. EXACT width shrinkwrap of text causes ellipsis to appear. - maxWidth = max(maxWidth + 1, 40); - // Set column width back to original width - columnSizer.setWidth(originalWidth); - return maxWidth; - }, - getPositionByEvent: function(e) { - var me = this, - cellNode = e.getTarget(me.cellSelector), - rowNode = e.getTarget(me.itemSelector), - record = me.getRecord(rowNode), - header = me.getHeaderByCell(cellNode); - return me.getPosition(record, header); - }, - getHeaderByCell: function(cell) { - if (cell) { - return this.ownerCt.getVisibleColumnManager().getHeaderById(Ext.getDom(cell).getAttribute('data-columnId')); - } - return false; - }, - /** - * @param {Ext.grid.CellContext} position The current navigation position. - * @param {String} direction 'up', 'down', 'right' and 'left' - * @param {Function} [verifierFn] A function to verify the validity of the calculated position. - * When using this function, you must return true to allow the newPosition to be returned. - * @param {Ext.grid.CellContext} [verifierFn.position] The calculated new position to verify. - * @param {Object} [scope] Scope (`this` context) to run the verifierFn in. Defaults to this View. - * @return {Ext.grid.CellContext} An object encapsulating the unique cell position. - * - * @private - */ - walkCells: function(pos, direction, verifierFn, scope) { - var me = this, - result = pos.clone(), - lockingPartner = me.lockingPartner && me.lockingPartner.grid.isVisible() ? me.lockingPartner : null, - rowIdx = pos.rowIdx, - maxRowIdx = me.dataSource.getCount() - 1, - columns = me.ownerCt.getVisibleColumnManager().getColumns(); - switch (direction.toLowerCase()) { - case 'right': - // At end of row. - if (pos.isLastColumn()) { - // If we're at the end of the locked view, same row, else wrap downwards - rowIdx = lockingPartner && me.isLockedView ? rowIdx : rowIdx + 1; - // If stepped past the bottom row, deny the action - if (rowIdx > maxRowIdx) { - return false; - } - // There's a locking partner to move into - if (lockingPartner) { - result.view = lockingPartner; - } - result.setPosition(rowIdx, 0); - } else // Not at end, just go forwards one column - { - result.navigate(+1); - }; - break; - case 'left': - // At start of row. - if (pos.isFirstColumn()) { - // If we're at the start of the normal view, same row, else wrap upwards - rowIdx = lockingPartner && me.isNormalView ? rowIdx : rowIdx - 1; - // If top row, deny up - if (rowIdx < 0) { - return false; - } - // There's a locking partner to move into - if (lockingPartner) { - result.view = lockingPartner; - columns = lockingPartner.getVisibleColumnManager().getColumns(); - } - result.setPosition(rowIdx, columns[columns.length - 1]); - } else // Not at end, just go backwards one column - { - result.navigate(-1); - }; - break; - case 'up': - // if top row, deny up - if (rowIdx === 0) { - return false; - } else // go up - { - result.setRow(rowIdx - 1); - }; - break; - case 'down': - // if bottom row, deny down - if (rowIdx === maxRowIdx) { - return false; - } else // go down - { - result.setRow(rowIdx + 1); - }; - break; - } - if (verifierFn && verifierFn.call(scope || me, result) !== true) { - return false; - } - return result; - }, - /** - * Increments the passed row index by the passed increment which may be +ve or -ve - * - * Skips hidden rows. - * - * If no row is visible in the specified direction, returns the input row index unchanged. - * @param {Number} startRow The zero-based row index to start from. - * @param {Number} distance The distance to move the row by. May be +ve or -ve. - * @deprecated 5.5.0 - * @private - */ - walkRows: function(startRow, distance) { - // Note that we use the **dataSource** here because row indices mean view row indices - // so records in collapsed groups must be omitted. - var me = this, - store = me.dataSource, - moved = 0, - lastValid = startRow, - node, - limit = (distance < 0) ? 0 : store.getCount() - 1, - increment = limit ? 1 : -1, - result = startRow; - do { - if (limit ? result >= limit : result <= limit) { - return lastValid || limit; - } - result += increment; - if ((node = Ext.fly(me.getRow(result))) && node.isVisible(true)) { - moved += increment; - lastValid = result; - } - } while (// Walked off the end: return the last encountered valid row - // Move the result pointer on by one position. We have to count intervening VISIBLE nodes - // Stepped onto VISIBLE record: Increment the moved count. - // We must not count stepping onto a non-rendered record as a move. - moved !== distance); - return result; - }, - /** - * Navigates from the passed record by the passed increment which may be +ve or -ve - * - * Skips hidden records. - * - * If no record is visible in the specified direction, returns the starting record index unchanged. - * @param {Ext.data.Model} startRec The Record to start from. - * @param {Number} distance The distance to move from the record. May be +ve or -ve. - */ - walkRecs: function(startRec, distance) { - // Note that we use the **store** to access the records by index because the dataSource omits records in collapsed groups. - // This is used by selection models which use the **store** - var me = this, - store = me.dataSource, - moved = 0, - lastValid = startRec, - node, - limit = (distance < 0) ? 0 : (store.isBufferedStore ? store.getTotalCount() : store.getCount()) - 1, - increment = limit ? 1 : -1, - testIndex = store.indexOf(startRec), - rec; - do { - if (limit ? testIndex >= limit : testIndex <= limit) { - return lastValid; - } - testIndex += increment; - rec = store.getAt(testIndex); - if (!rec.isCollapsedPlaceholder && (node = Ext.fly(me.getNodeByRecord(rec))) && node.isVisible(true)) { - moved += increment; - lastValid = rec; - } - } while (// Walked off the end: return the last encountered valid record - // Move the result pointer on by one position. We have to count intervening VISIBLE nodes - // Stepped onto VISIBLE record: Increment the moved count. - // We must not count stepping onto a non-rendered record as a move. - moved !== distance); - return lastValid; - }, - /** - * Returns the index of the first row in your table view deemed to be visible. - * @return {Number} - * @private - */ - getFirstVisibleRowIndex: function() { - var me = this, - count = (me.dataSource.isBufferedStore ? me.dataSource.getTotalCount() : me.dataSource.getCount()), - result = me.indexOf(me.all.first()) - 1; - do { - result += 1; - if (result === count) { - return; - } - } while (!Ext.fly(me.getRow(result)).isVisible(true)); - return result; - }, - /** - * Returns the index of the last row in your table view deemed to be visible. - * @return {Number} - * @private - */ - getLastVisibleRowIndex: function() { - var me = this, - result = me.indexOf(me.all.last()); - do { - result -= 1; - if (result === -1) { - return; - } - } while (!Ext.fly(me.getRow(result)).isVisible(true)); - return result; - }, - getHeaderCt: function() { - return this.headerCt; - }, - getPosition: function(record, header) { - return new Ext.grid.CellContext(this).setPosition(record, header); - }, - onDestroy: function() { - var me = this, - features = me.featuresMC, - len, i; - if (features) { - for (i = 0 , len = features.getCount(); i < len; ++i) { - features.getAt(i).destroy(); - } - } - me.cellFly = me.featuresMC = null; - me.callParent(arguments); - me.all.destroy(); - me.body.destroy(); - me.all = me.body = me.body.el = null; - me.grid = me.ownerGrid = me.headerCt = me.panel = null; - me.selection = me.actionPosition = me.eventPosition = me.lastFocused = null; - me.actionRow = me.cellTpl = me.rowTpl = me.bufferedRenderer = null; - }, - /** - * @private. - * Respond to store replace event which is fired by GroupStore group expand/collapse operations. - * This saves a layout because a remove and add operation are coalesced in this operation. - */ - onReplace: function(store, startIndex, oldRecords, newRecords) { - var me = this, - bufferedRenderer = me.bufferedRenderer, - restoreFocus; - // If there's a buffered renderer and the removal range falls inside the current view... - if (me.rendered && bufferedRenderer) { - // If focus was in any way in the view, whether actionable or navigable, this will return - // a function which will restore that state. - restoreFocus = me.saveFocusState(); - bufferedRenderer.onReplace(store, startIndex, oldRecords, newRecords); - // If focus was in any way in this view, this will restore it - restoreFocus(); - } else { - me.callParent(arguments); - } - me.setPendingStripe(startIndex); - }, - onResize: function(width, height, oldWidth, oldHeight) { - var me = this, - bufferedRenderer = me.bufferedRenderer; - // Ensure the buffered renderer makes its adjustments before user resize listeners - if (bufferedRenderer) { - bufferedRenderer.onViewResize(me, width, height, oldWidth, oldHeight); - } - me.callParent([ - width, - height - ]); - }, - // after adding a row stripe rows from then on - onAdd: function(store, records, index) { - var me = this, - bufferedRenderer = me.bufferedRenderer; - // Only call the buffered renderer's handler if there's a need to. - // That is if the rendered block has been moved down the dataset, or - // the addition will tip the rendered block size over the buffered renderer's calculated viewSize. - if (me.rendered && bufferedRenderer && (bufferedRenderer.bodyTop || me.dataSource.getCount() + records.length >= bufferedRenderer.viewSize)) { - bufferedRenderer.onReplace(store, index, [], records); - } else { - me.callParent(arguments); - } - me.setPendingStripe(index); - }, - // after removing a row stripe rows from then on - onRemove: function(store, records, index) { - var me = this, - bufferedRenderer = me.bufferedRenderer, - restoreFocus; - // If there's a BufferedRenderer, and it's being used (dataset size before removal was >= rendered block size)... - if (me.rendered && bufferedRenderer && me.dataSource.getCount() + records.length >= bufferedRenderer.viewSize) { - // If focus was in any way in the view, whether actionable or navigable, this will return - // a function which will restore that state. - restoreFocus = me.saveFocusState(); - bufferedRenderer.onReplace(store, index, records, []); - // If focus was in any way in this view, this will restore it - restoreFocus(); - } else { - me.callParent(arguments); - } - me.setPendingStripe(index); - }, - /** - * @private - * Called prior to an operation which may remove focus from this view by some kind of DOM operation. - * - * If this view contains focus in any sense, either navigable mode, or actionable mode, - * this method returns a function which, when called after the disruptive DOM operation - * will restore focus to the same record/column, or, if the record has been removed, to the same - * row index/column. - * - * @returns {Function} A function that will restore focus if focus was within this view, - * or a function which does nothing is focus is not in this view. - */ - saveFocusState: function() { - var me = this, - store = me.dataSource, - actionableMode = me.actionableMode, - navModel = me.getNavigationModel(), - focusPosition = actionableMode ? me.actionPosition : navModel.getPosition(true), - refocusRow, refocusCol; - if (focusPosition) { - // Separate this from the instance that the nav model is using. - focusPosition = focusPosition.clone(); - // Exit actionable mode. - // We must inform any Actionables that they must relinquish control. - // Tabbability must be reset. - if (actionableMode) { - me.ownerGrid.setActionableMode(false); - } - // Blur the focused descendant, but do not trigger focusLeave. - me.el.dom.focus(); - // Exiting actionable mode navigates to the owning cell, so in either focus mode we must - // clear the navigation position - navModel.setPosition(); - // The following function will attempt to refocus back in the same mode to the same cell - // as it was at before based upon the previous record (if it's still inthe store), or the row index. - return function() { - // If we still have data, attempt to refocus in the same mode. - if (store.getCount()) { - // Adjust expectations of where we are able to refocus according to what kind of destruction - // might have been wrought on this view's DOM during focus save. - refocusRow = Math.min(focusPosition.rowIdx, me.all.getCount() - 1); - refocusCol = Math.min(focusPosition.colIdx, me.getVisibleColumnManager().getColumns().length - 1); - focusPosition = new Ext.grid.CellContext(me).setPosition(store.contains(focusPosition.record) ? focusPosition.record : refocusRow, refocusCol); - if (actionableMode) { - me.ownerGrid.setActionableMode(true, focusPosition); - } else { - me.cellFocused = true; - // Pass "preventNavigation" as true so that that does not cause selection. - navModel.setPosition(focusPosition, null, null, null, true); - } - } else // No rows - focus associated column header - { - focusPosition.column.focus(); - } - }; - } - return Ext.emptyFn; - }, - onDataRefresh: function(store) { - // When there's a buffered renderer present, store refresh events cause TableViews to - // go to scrollTop:0 - var me = this, - owner = me.ownerCt; - // If triggered during an animation, refresh once we're done - if (owner && owner.isCollapsingOrExpanding === 2) { - owner.on('expand', me.onDataRefresh, me, { - single: true - }); - return; - } - me.callParent([ - store - ]); - }, - getViewRange: function() { - var me = this; - if (me.bufferedRenderer) { - return me.bufferedRenderer.getViewRange(); - } - return me.callParent(); - }, - setPendingStripe: function(index) { - var current = this.stripeOnUpdate; - if (current === null) { - current = index; - } else { - current = Math.min(current, index); - } - this.stripeOnUpdate = current; - }, - onEndUpdate: function() { - var me = this, - stripeOnUpdate = me.stripeOnUpdate, - startIndex = me.all.startIndex; - if (me.rendered && (stripeOnUpdate || stripeOnUpdate === 0)) { - if (stripeOnUpdate < startIndex) { - stripeOnUpdate = startIndex; - } - me.doStripeRows(stripeOnUpdate); - me.stripeOnUpdate = null; - } - me.callParent(arguments); - }, - /** - * Stripes rows from a particular row index. - * @param {Number} startRow - * @param {Number} [endRow] argument specifying the last row to process. - * By default process up to the last row. - * @private - */ - doStripeRows: function(startRow, endRow) { - var me = this, - rows, rowsLn, i, row; - // ensure stripeRows configuration is turned on - if (me.rendered && me.stripeRows) { - rows = me.getNodes(startRow, endRow); - for (i = 0 , rowsLn = rows.length; i < rowsLn; i++) { - row = rows[i]; - // Remove prior applied row classes. - row.className = row.className.replace(me.rowClsRe, ' '); - startRow++; - // Every odd row will get an additional cls - if (startRow % 2 === 0) { - row.className += (' ' + me.altRowCls); - } - } - } - }, - hasActiveFeature: function() { - return (this.isGrouping && this.store.isGrouped()) || this.isRowWrapped; - }, - getCellPaddingAfter: function(cell) { - return Ext.fly(cell).getPadding('r'); - }, - privates: { - refreshScroll: function() { - var me = this, - bufferedRenderer = me.bufferedRenderer; - // If there is a BufferedRenderer, it must take care of the virtual - // scroll range by either adding a stretcher or telling the TouchScroller - // about the virtual content height and width. - if (bufferedRenderer) { - bufferedRenderer.refreshSize(); - } else { - me.callParent(); - } - }, - /* - * Overridden implementation. - * Called by refresh to collect the view item nodes. - * Note that these may be wrapping rows which *contain* rows which map to records - * @private - */ - collectNodes: function(targetEl) { - this.all.fill(this.getNodeContainer().childNodes, this.all.startIndex); - }, - /** - * - * @param {Boolean} enabled - * @param {Ext.grid.CellContext} position - * @return {Boolean} Returns `false` if the mode did not change. - * @private - */ - setActionableMode: function(enabled, position) { - var me = this, - navModel = me.getNavigationModel(), - focusRow, focusCell, tabbableChildren, activeEl, - actionables = me.grid.actionables, - len = actionables.length, - i, record, isActionable, lockingPartner, doRestoreFocus; - // No mode change. - // ownerGrid's call will NOT fire mode change event upon false return. - if (me.actionableMode === enabled) { - // If we're not actinoable already, or (we are actionable already at that position) return false. - // Test using mandatory passed position because we may not have an actionPosition if we are - // the lockingPartner of an actionable view that contained the action position. - // - // If we being told to go into actionable mode but at another position, we must continue. - // This is just actionable navigation. - if (!enabled || position.isEqual(me.actionPosition)) { - return false; - } - } - // If this View or its lockingPartner contains the current focus position, then make the tab bumpers tabbable - // and move them to surround the focused row. - if (enabled) { - if (position && (position.view === me || (position.view === (lockingPartner = me.lockingPartner) && lockingPartner.actionableMode))) { - position = position.clone(); - record = position.record; - me.grid.ensureVisible(record, { - column: position.column - }); - focusRow = me.all.item(position.rowIdx); - // We're the focused side - attempt to see if ths focused cell is actionable - if (!lockingPartner) { - focusCell = Ext.fly(focusRow).down(position.column.getCellSelector()); - // Inform all Actionables that we intend to activate this cell. - // If any return true, isActionable will be set - for (i = 0; i < len; i++) { - isActionable = isActionable || actionables[i].activateCell(position); - } - } - // If we have a lockingPartner that is actionable - // or if we find some elements we can restore to tabbability - // or a plugin declared it was actionable at this position: - // dive in and activate the row - if (lockingPartner || focusCell.restoreTabbableState(/* skipSelf */ - true).length || isActionable) { - // We are entering actionable mode. - // Tell all registered Actionables about this fact if they need to know. - for (i = 0; i < len; i++) { - if (actionables[i].activateRow) { - actionables[i].activateRow(focusRow); - } - } - // Only enter actionable mode if there is an already actionable locking partner, - // or there are tabbable children in current cell. - if (lockingPartner || (tabbableChildren = focusCell.findTabbableElements()).length) { - // Restore tabbabilty to all elements in this row - focusRow.restoreTabbableState(/* skipSelf */ - true); - // If we are the locking partner of an actionable side, we are successful already. - // But we must not have an actionPosition. We are not actually in possession of an active cell - // and we must not reject an action request at that cell in the isEqual test above. - if (lockingPartner) { - me.actionableMode = true; - me.actionPosition = null; - return true; - } - // If there are focusables in the actioned cell, we can enter actionable mode. - if (tabbableChildren) { - /** - * @property {Ext.dom.Element} actionRow - * Only valid when a view is in actionableMode. The currently actioned row - */ - me.actionRow = focusRow; - me.actionableMode = me.ownerGrid.actionableMode = true; - // Clear current position on entry into actionable mode - navModel.setPosition(); - navModel.actionPosition = me.actionPosition = position; - Ext.fly(tabbableChildren[0]).focus(); - // Avoid falling through to returning false - return true; - } - } - } - } - // Did not enter actionable mode. - // ownerGrid's call will NOT fire mode change event upon false return. - return false; - } else { - // Capture before exiting from actionable mode moves focus - activeEl = Ext.fly(Ext.Element.getActiveElement()); - doRestoreFocus = me.el.contains(activeEl); - // Blur the focused descendant, but do not trigger focusLeave. - // This is so that when the focus is restored to the cell which contained - // the active content, it will not be a FocusEnter from the universe. - if (doRestoreFocus) { - // Row to return focus to. - record = me.actionPosition && me.actionPosition.record; - } - // We are exiting actionable mode. - // Tell all registered Actionables about this fact if they need to know. - for (i = 0; i < len; i++) { - if (actionables[i].deactivate) { - actionables[i].deactivate(); - } - } - // If we had begun action (we may be a dormant lockingPartner), make any tabbables untabbable - if (me.actionRow) { - me.actionRow.saveTabbableState({ - skipSelf: true, - includeSaved: false - }); - } - // These flags MUST be set before focus restoration to the owning cell. - // so that when Ext.grid.NavigationModel#onFocusMove attempts to exit actionable mode, we don't recurse. - me.actionableMode = me.ownerGrid.actionableMode = false; - me.actionPosition = navModel.actionPosition = me.actionRow = null; - // Restore focus to the cell in which actionable mode is active. - if (doRestoreFocus) { - navModel.setPosition(new Ext.grid.CellContext(me).setPosition(record || 0, me.getHeaderByCell(activeEl.findParent(me.getCellSelector())) || 0)); - } - } - }, - onRowExit: function(prevRow, newRow, forward) { - var me = this, - direction = forward ? 'nextSibling' : 'previousSibling', - lockingPartner = me.lockingPartner, - actionables = me.grid.actionables, - len = actionables.length, - i, rowIdx, cellIdx; - // The refreshing flag is so that onFocusLeave ignores any focus leaves caused - // by deactivation/hiding/DOM motion of any focusables here, and - // does not exit actionable mode. - // The actionableModeTabbing flag prevents the NavigationModel from exiting actionable mode on focus move. - me.refreshing = me.actionableModeTabbing = true; - // Tell all registered Actionables to deactivate since we are leaving the row... - // if they need to know. - for (i = 0; i < len; i++) { - if (actionables[i].deactivate) { - actionables[i].deactivate(); - } - } - if (lockingPartner && lockingPartner.grid.isVisible()) { - rowIdx = me.all.indexOf(prevRow); - // TAB out of right side of view - if (forward) { - cellIdx = 0; - // If normal side go to next row in locked side - if (me.isNormalView) { - rowIdx++; - } - } else // TAB out of left side of view - { - cellIdx = lockingPartner.getVisibleColumnManager().getColumns().length - 1; - // If locked side go to previous row in normal side - if (me.isLockedView) { - rowIdx--; - } - } - // We've switched sides. - me.actionPosition = null; - me = lockingPartner; - newRow = me.all.item(rowIdx, true); - } - // Activate the next row. - // This moves actionables' tabbable items to next row, restores that row's tabbability - // and focuses the first/last tabbable element it finds depending on direction. - me.findFirstActionableElement(newRow, direction, forward); - me.refreshing = me.actionableModeTabbing = false; - // Deactivate remaining tabbables in the row we are exitting. - Ext.fly(prevRow).saveTabbableState({ - skipSelf: true, - includeSaved: false - }); - }, - /** - * Finds the first actionable element in the passed direction starting by looking in the passed row. - * @private - */ - findFirstActionableElement: function(focusRow, direction, forward) { - var me = this, - columns = me.getVisibleColumnManager().getColumns(), - columnCount = columns.length, - focusCell, focusTarget, - actionables = me.grid.actionables, - actionableCount = actionables.length, - i, j, column, - position = new Ext.grid.CellContext(me), - isActionable, tabbableChildren; - if (focusRow) { - position.setRow(focusRow); - for (i = 0; i < actionableCount; i++) { - // Tell all actionables who need to know that we are moving actionable mode to a new row. - // They should insert any tabbable elements into appropriate cells in the row. - if (actionables[i].activateRow) { - actionables[i].activateRow(focusRow); - } - } - // Look through the columns until we find one where the Actionables return that the cell is actionable - // or there are tabbable elements found. - for (i = (forward ? 0 : columnCount - 1); (forward ? i < columnCount : i > -1) && !focusTarget; i = i + (forward ? 1 : -1)) { - column = columns[i]; - position.setColumn(column); - focusCell = Ext.fly(focusRow).down(position.column.getCellSelector()); - for (j = 0; j < actionableCount; j++) { - isActionable = isActionable || actionables[j].activateCell(position); - } - // If there are restored tabbable elements rendered in the cell, or an Actionable is activated on this cell... - if (focusCell.restoreTabbableState(/* skipSelf */ - true).length || isActionable) { - tabbableChildren = focusCell.findTabbableElements(); - me.actionRow = Ext.get(focusRow); - // Restore tabbabilty to all elements in this row. - me.actionRow.restoreTabbableState(/* skipSelf */ - true); - focusTarget = tabbableChildren[forward ? 0 : tabbableChildren.length - 1]; - } - } - // Found a focusable element, focus it. - if (focusTarget) { - // Keep actionPosition synched - me.actionPosition = me.getNavigationModel().actionPosition = position; - Ext.fly(focusTarget).focus(); - } else { - // We walked off the end of the columns without finding a focusTarget - // Process onRowExit in the current direction - me.onRowExit(focusRow, me.all.item(position.rowIdx + (forward ? 1 : -1)), forward); - } - } else // No focusRow, loop round in the correct direction. - { - me.grid.ensureVisible(forward ? 0 : me.dataSource.getCount() - 1, { - callback: function(success, record, row) { - if (success) { - me.findFirstActionableElement(row, direction, forward); - } else { - me.ownerGrid.setActionableMode(false); - } - } - }); - } - } - } -}); - -/** - * Grids are an excellent way of showing large amounts of tabular data on the client side. - * Essentially a supercharged ``, GridPanel makes it easy to fetch, sort and filter - * large amounts of data. - * - * Grids are composed of two main pieces - a {@link Ext.data.Store Store} full of data and - * a set of columns to render. - * - * ## Basic GridPanel - * - * @example - * Ext.create('Ext.data.Store', { - * storeId: 'simpsonsStore', - * fields:[ 'name', 'email', 'phone'], - * data: [ - * { name: 'Lisa', email: 'lisa@simpsons.com', phone: '555-111-1224' }, - * { name: 'Bart', email: 'bart@simpsons.com', phone: '555-222-1234' }, - * { name: 'Homer', email: 'homer@simpsons.com', phone: '555-222-1244' }, - * { name: 'Marge', email: 'marge@simpsons.com', phone: '555-222-1254' } - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Simpsons', - * store: Ext.data.StoreManager.lookup('simpsonsStore'), - * columns: [ - * { text: 'Name', dataIndex: 'name' }, - * { text: 'Email', dataIndex: 'email', flex: 1 }, - * { text: 'Phone', dataIndex: 'phone' } - * ], - * height: 200, - * width: 400, - * renderTo: Ext.getBody() - * }); - * - * The code above produces a simple grid with three columns. We specified a Store which - * will load JSON data inline. - * In most apps we would be placing the grid inside another container and wouldn't need to - * use the {@link #height}, {@link #width} and {@link #renderTo} configurations but they - * are included here to make it easy to get up and running. - * - * The grid we created above will contain a header bar with a title ('Simpsons'), a row of - * column headers directly underneath and finally the grid rows under the headers. - * - * **Height config with bufferedRenderer: true** - * - * The {@link #height} config must be set when creating a grid using - * {@link #bufferedRenderer bufferedRenderer}: true _and_ the grid's height is not managed - * by an owning container layout. In Ext JS 5.x bufferedRendering is true by default. - * - * ## Configuring columns - * - * By default, each column is sortable and will toggle between ASC and DESC sorting when - * you click on its header. Each column header is also reorderable by default, and each - * gains a drop-down menu with options to hide and show columns. It's easy to configure - * each column - here we use the same example as above and just modify the columns config: - * - * columns: [ - * { - * text: 'Name', - * dataIndex: 'name', - * sortable: false, - * hideable: false, - * flex: 1 - * }, - * { - * text: 'Email', - * dataIndex: 'email', - * hidden: true - * }, - * { - * text: 'Phone', - * dataIndex: 'phone', - * width: 100 - * } - * ] - * - * We turned off sorting and hiding on the 'Name' column so clicking its header now has no - * effect. We also made the Email column hidden by default (it can be shown again by using - * the menu on any other column). We also set the Phone column to a fixed with of 100px - * and flexed the Name column, which means it takes up all remaining width after the other - * columns have been accounted for. See the {@link Ext.grid.column.Column column docs} for - * more details. - * - * ## Renderers - * - * As well as customizing columns, it's easy to alter the rendering of individual cells - * using renderers. A renderer is tied to a particular column and is passed the value that - * would be rendered into each cell in that column. For example, we could define a - * renderer function for the email column to turn each email address into a mailto link: - * - * columns: [ - * { - * text: 'Email', - * dataIndex: 'email', - * renderer: function(value) { - * return Ext.String.format('{1}', value, value); - * } - * } - * ] - * - * See the {@link Ext.grid.column.Column column docs} for more information on renderers. - * - * ## Selection Models - * - * Sometimes you simply want to render data for viewing, but usually it's - * necessary to interact with or update that data. Grids use a concept called a Selection - * Model, which is simply a mechanism for selecting some part of the data in the grid. The - * two main types of Selection Model are RowSelectionModel, where entire rows are - * selected, and CellSelectionModel, where individual cells are selected. - * - * Grids use a Row Selection Model by default, but this is easy to customize like so: - * - * Ext.create('Ext.grid.Panel', { - * selModel: 'cellmodel', - * store: ... - * }); - * - * - * Specifying the `cellmodel` changes a couple of things. Firstly, clicking on a cell now - * selects just that cell (using a {@link Ext.selection.RowModel rowmodel} will select the - * entire row), and secondly the keyboard navigation will walk from cell to cell instead - * of row to row. Cell-based selection models are usually used in conjunction with - * editing. - * - * You may also utilize selModel as a config object for an instance of {@link Ext.selection.Model}. - * - * For example: - * - * selModel: { - * selType: 'cellmodel', - * mode : 'MULTI' - * } - * - * This allows you to modify additional selection model configurations such as: - * - * + {@link Ext.selection.Model#mode mode} - Specifies whether user may select multiple - * rows or single rows - * + {@link Ext.selection.Model#allowDeselect allowDeselect} - Specifies whether user may - * deselect records (when in SINGLE mode) - * + {@link Ext.selection.Model#ignoreRightMouseSelection ignoreRightMouseSelection} - Specifies - * whether user may ignore right clicks - * for selection purposes - * - * ## Sorting & Filtering - * - * Every grid is attached to a {@link Ext.data.Store Store}, which provides multi-sort and - * filtering capabilities. It's - * easy to set up a grid to be sorted from the start: - * - * var myGrid = Ext.create('Ext.grid.Panel', { - * store: { - * fields: ['name', 'email', 'phone'], - * sorters: ['name', 'phone'] - * }, - * columns: [ - * { text: 'Name', dataIndex: 'name' }, - * { text: 'Email', dataIndex: 'email' } - * ] - * }); - * - * Sorting at run time is easily accomplished by simply clicking each column header. If - * you need to perform sorting on more than one field at run time it's easy to do so by - * adding new sorters to the store: - * - * myGrid.store.sort([ - * { property: 'name', direction: 'ASC' }, - * { property: 'email', direction: 'DESC' } - * ]); - * - * See {@link Ext.data.Store} for examples of filtering. - * - * ## State saving - * - * When configured {@link #stateful}, grids save their column state (order and width) - * encapsulated within the default Panel state of changed width and height and - * collapsed/expanded state. - * - * On a `stateful` grid, not only should the Grid have a {@link #stateId}, each - * {@link #columns column} of the grid should also be configured with a - * {@link Ext.grid.column.Column#stateId stateId} which identifies that column locally - * within the grid. - * - * Omitting the `stateId` config from the columns results in columns with generated - * internal ID's. The generated ID's are subject to change on each page load - * making it impossible for the state manager to restore the previous state of the - * columns. - * - * ## Plugins and Features - * - * Grid supports addition of extra functionality through features and plugins: - * - * - {@link Ext.grid.plugin.CellEditing CellEditing} - editing grid contents one cell at a time. - * - * - {@link Ext.grid.plugin.RowEditing RowEditing} - editing grid contents an entire row at a time. - * - * - {@link Ext.grid.plugin.DragDrop DragDrop} - drag-drop reordering of grid rows. - * - * - {@link Ext.toolbar.Paging Paging toolbar} - paging through large sets of data. - * - * - {@link Ext.grid.plugin.BufferedRenderer Infinite scrolling} - another way to handle large sets of data. - * - * - {@link Ext.grid.RowNumberer RowNumberer} - automatically numbered rows. - * - * - {@link Ext.grid.feature.Grouping Grouping} - grouping together rows having the same value in a particular field. - * - * - {@link Ext.grid.feature.Summary Summary} - a summary row at the bottom of a grid. - * - * - {@link Ext.grid.feature.GroupingSummary GroupingSummary} - a summary row at the bottom of each group. - */ -Ext.define('Ext.grid.Panel', { - extend: 'Ext.panel.Table', - requires: [ - 'Ext.view.Table' - ], - alias: [ - 'widget.gridpanel', - 'widget.grid' - ], - alternateClassName: [ - 'Ext.list.ListView', - 'Ext.ListView', - 'Ext.grid.GridPanel' - ], - viewType: 'tableview', - lockable: false, - /** - * @cfg {Boolean} rowLines False to remove row line styling - */ - rowLines: true -}); -// Columns config is required in Grid -/** - * @cfg {Ext.grid.column.Column[]/Object} columns (required) - * @inheritdoc - */ -/** - * @event beforereconfigure - * Fires before a reconfigure to enable modification of incoming Store and columns. - * @param {Ext.grid.Panel} this - * @param {Ext.data.Store} store The store that was passed to the {@link #method-reconfigure} method - * @param {Object[]} columns The column configs that were passed to the {@link #method-reconfigure} method - * @param {Ext.data.Store} oldStore The store that will be replaced - * @param {Ext.grid.column.Column[]} oldColumns The column headers that will be replaced. - */ -/** - * @event reconfigure - * Fires after a reconfigure. - * @param {Ext.grid.Panel} this - * @param {Ext.data.Store} store The store that was passed to the {@link #method-reconfigure} method - * @param {Object[]} columns The column configs that were passed to the {@link #method-reconfigure} method - * @param {Ext.data.Store} oldStore The store that was replaced - * @param {Ext.grid.column.Column[]} oldColumns The column headers that were replaced. - */ - -/** - * @private - * Private Container class used by the {@link Ext.grid.RowEditor} to hold its buttons. - */ -Ext.define('Ext.grid.RowEditorButtons', { - extend: 'Ext.container.Container', - alias: 'widget.roweditorbuttons', - frame: true, - shrinkWrap: true, - position: 'bottom', - constructor: function(config) { - var me = this, - rowEditor = config.rowEditor, - cssPrefix = Ext.baseCSSPrefix, - plugin = rowEditor.editingPlugin; - config = Ext.apply({ - baseCls: cssPrefix + 'grid-row-editor-buttons', - defaults: { - xtype: 'button', - ui: rowEditor.buttonUI, - scope: plugin, - flex: 1, - minWidth: Ext.panel.Panel.prototype.minButtonWidth - }, - items: [ - { - cls: cssPrefix + 'row-editor-update-button', - itemId: 'update', - handler: plugin.completeEdit, - text: rowEditor.saveBtnText, - disabled: rowEditor.updateButtonDisabled, - listeners: { - element: 'el', - keydown: me.onUpdateKeyDown, - scope: me - } - }, - { - cls: cssPrefix + 'row-editor-cancel-button', - itemId: 'cancel', - handler: plugin.cancelEdit, - text: rowEditor.cancelBtnText, - listeners: { - element: 'el', - keydown: me.onCancelKeyDown, - scope: me - } - } - ] - }, config); - me.callParent([ - config - ]); - me.addClsWithUI(me.position); - }, - // SHIFT+TAB off the update button loops back into the last field. - onUpdateKeyDown: function(e) { - if (e.shiftKey && e.getKey() === e.TAB) { - e.stopEvent(); - // Must delay the focus, otherwise the imminent keyup will TAB off that field - this.rowEditor.child(':focusable:not([isButton]):last').focus(false, true); - } - }, - // TAB off the cancel button loops back into the first field. - onCancelKeyDown: function(e) { - if (!e.shiftKey && e.getKey() === e.TAB) { - e.stopEvent(); - // Must delay the focus, otherwise the imminent keyup will TAB off that field - this.rowEditor.child(':focusable').focus(false, true); - } - }, - setButtonPosition: function(position) { - var me = this, - rowEditor = this.rowEditor, - rowEditorHeight = rowEditor.getHeight(), - rowEditorBody = rowEditor.body, - bottom = '', - top = ''; - me.removeClsWithUI(me.position); - me.position = position; - me.addClsWithUI(position); - // we tried setting the top/bottom value in the stylesheet based on form field - // height + row editor padding, but that approach does not work when there are - // larger things inside the editor, e.g. textarea, so we have to measure - // the row editor height and position the buttons accordingly (see EXTJSIV-9914). - if (position === 'top') { - bottom = (rowEditorHeight - rowEditorBody.getBorderWidth('t')) + 'px'; - } else { - top = (rowEditorHeight - rowEditorBody.getBorderWidth('b')) + 'px'; - } - me.el.setStyle({ - top: top, - bottom: bottom - }); - }, - privates: { - getFramingInfoCls: function() { - return this.baseCls + '-' + this.ui + '-' + this.position; - }, - getFrameInfo: function() { - var frameInfo = this.callParent(); - // Trick Renderable into rendering the top framing elements, even though they - // are not needed in the default "bottom" position. This allows us to flip the - // buttons into "top" position without re-rendering. - frameInfo.top = true; - return frameInfo; - } - } -}); - -// Currently has the following issues: -// - Does not handle postEditValue -// - Fields without editors need to sync with their values in Store -// - starting to edit another record while already editing and dirty should probably prevent it -// - aggregating validation messages -// - tabIndex is not managed bc we leave elements in dom, and simply move via positioning -// - layout issues when changing sizes/width while hidden (layout bug) -/** - * Internal utility class used to provide row editing functionality. For developers, they should use - * the RowEditing plugin to use this functionality with a grid. - * - * @private - */ -Ext.define('Ext.grid.RowEditor', { - extend: 'Ext.form.Panel', - alias: 'widget.roweditor', - requires: [ - 'Ext.tip.ToolTip', - 'Ext.util.KeyNav', - 'Ext.grid.RowEditorButtons' - ], - // - saveBtnText: 'Update', - // - // - cancelBtnText: 'Cancel', - // - // - errorsText: 'Errors', - // - // - dirtyText: 'You need to commit or cancel your changes', - // - lastScrollLeft: 0, - lastScrollTop: 0, - border: false, - _wrapCls: Ext.baseCSSPrefix + 'grid-row-editor-wrap', - errorCls: Ext.baseCSSPrefix + 'grid-row-editor-errors-item', - buttonUI: 'default', - // Change the hideMode to offsets so that we get accurate measurements when - // the roweditor is hidden for laying out things like a TriggerField. - hideMode: 'offsets', - _cachedNode: false, - initComponent: function() { - var me = this, - grid = me.editingPlugin.grid, - Container = Ext.container.Container, - form, normalCt, lockedCt; - me.cls = Ext.baseCSSPrefix + 'grid-editor ' + Ext.baseCSSPrefix + 'grid-row-editor'; - me.layout = { - type: 'hbox', - align: 'middle' - }; - me.lockable = grid.lockable; - // Create field containing structure for when editing a lockable grid. - if (me.lockable) { - me.items = [ - // Locked columns container shrinkwraps the fields - lockedCt = me.lockedColumnContainer = new Container({ - id: grid.id + '-locked-editor-cells', - scrollable: { - x: false, - y: false - }, - layout: { - type: 'hbox', - align: 'middle' - }, - // Locked grid has a border, we must be exactly the same width - margin: '0 1 0 0' - }), - // Normal columns container flexes the remaining RowEditor width - normalCt = me.normalColumnContainer = new Container({ - // not user scrollable, but needs a Scroller instance for syncing with view - scrollable: { - x: false, - y: false - }, - flex: 1, - id: grid.id + '-normal-editor-cells', - layout: { - type: 'hbox', - align: 'middle' - } - }) - ]; - // keep horizontal position of fields in sync with view's horizontal scroll position - lockedCt.getScrollable().addPartner(grid.lockedGrid.view.getScrollable(), 'x'); - normalCt.getScrollable().addPartner(grid.normalGrid.view.getScrollable(), 'x'); - } else { - // initialize a scroller instance for maintaining horizontal scroll position - me.setScrollable({ - x: false, - y: false - }); - // keep horizontal position of fields in sync with view's horizontal scroll position - me.getScrollable().addPartner(grid.view.getScrollable(), 'x'); - me.lockedColumnContainer = me.normalColumnContainer = me; - } - me.callParent(); - if (me.fields) { - me.addFieldsForColumn(me.fields, true); - me.insertColumnEditor(me.fields); - delete me.fields; - } - me.mon(Ext.GlobalEvents, { - scope: me, - show: me.repositionIfVisible - }); - form = me.getForm(); - form.trackResetOnLoad = true; - form.on('validitychange', me.onValidityChange, me); - form.on('errorchange', me.onErrorChange, me); - }, - // - // Grid listener added when this is rendered. - // Keep our containing element sized correctly - // - onGridResize: function() { - var me = this, - clientWidth = me.getClientWidth(), - grid = me.editingPlugin.grid, - gridBody = grid.body, - btns = me.getFloatingButtons(); - me.wrapEl.setLocalX(gridBody.getOffsetsTo(grid)[0] + gridBody.getBorderWidth('l') - grid.el.getBorderWidth('l')); - me.setWidth(clientWidth); - btns.setLocalX((clientWidth - btns.getWidth()) / 2); - if (me.lockable) { - me.lockedColumnContainer.setWidth(grid.lockedGrid.view.el.dom.clientWidth); - } - }, - syncAllFieldWidths: function() { - var me = this, - editors = me.query('[isEditorComponent]'), - len = editors.length, - column, i; - // In a locked grid, a RowEditor uses 2 inner containers, so need to use CQ to retrieve - // configured editors which were stamped with the isEditorComponent property in Editing.createColumnField - for (i = 0; i < len; ++i) { - column = editors[i].column; - if (column.isVisible()) { - me.onColumnShow(column); - } - } - }, - syncFieldWidth: function(column) { - var field = column.getEditor(), - width; - field._marginWidth = (field._marginWidth || field.el.getMargin('lr')); - width = column.getWidth() - field._marginWidth; - field.setWidth(width); - if (field.xtype === 'displayfield') { - // displayfield must have the width set on the inputEl for ellipsis to work - field.inputWidth = width; - } - }, - onValidityChange: function(form, valid) { - this.updateButton(valid); - }, - onErrorChange: function() { - var me = this, - valid; - if (me.errorSummary && me.isVisible()) { - valid = me.getForm().isValid(); - me[valid ? 'hideToolTip' : 'showToolTip'](); - } - }, - updateButton: function(valid) { - var buttons = this.floatingButtons; - if (buttons) { - buttons.child('#update').setDisabled(!valid); - } else { - // set flag so we can disabled when created if needed - this.updateButtonDisabled = !valid; - } - }, - afterRender: function() { - var me = this, - plugin = me.editingPlugin, - grid = plugin.grid, - view = grid.lockable ? grid.normalGrid.view : grid.view; - me.callParent(arguments); - // The scrollingViewEl is the TableView which scrolls - me.scrollingView = view; - me.scrollingViewEl = view.el; - view.on('scroll', me.onViewScroll, me); - // Prevent from bubbling click events to the grid view - me.mon(me.el, { - click: Ext.emptyFn, - stopPropagation: true - }); - // Ensure that the editor width always matches the total header width - me.mon(grid, 'resize', me.onGridResize, me); - if (me.lockable) { - grid.lockedGrid.view.on('resize', 'onGridResize', me); - } - me.el.swallowEvent([ - 'keypress', - 'keydown' - ]); - me.initKeyNav(); - me.mon(plugin.view, { - beforerefresh: me.onBeforeViewRefresh, - refresh: me.onViewRefresh, - itemremove: me.onViewItemRemove, - scope: me - }); - // Prevent trying to reposition while we set everything up - me.preventReposition = true; - me.syncAllFieldWidths(); - delete me.preventReposition; - }, - initKeyNav: function() { - var me = this, - plugin = me.editingPlugin; - me.keyNav = new Ext.util.KeyNav(me.el, { - tab: { - fn: me.onFieldTab, - scope: me - }, - enter: plugin.onEnterKey, - esc: plugin.onEscKey, - scope: plugin - }); - }, - onBeforeViewRefresh: function(view) { - var me = this, - viewDom = view.el.dom; - if (me.el.dom.parentNode === viewDom) { - viewDom.removeChild(me.el.dom); - } - }, - onViewRefresh: function(view) { - var me = this, - context = me.context, - row; - // Ignore refresh caused by the completion process - if (!me.completing) { - // Recover our row node after a view refresh - if (context && (row = view.getRow(context.record))) { - context.row = row; - me.reposition(); - if (me.tooltip && me.tooltip.isVisible()) { - me.tooltip.setTarget(context.row); - } - } else { - me.editingPlugin.cancelEdit(); - } - } - }, - onViewItemRemove: function(records, index, items, view) { - var me = this, - grid, store, gridView, context, record, plugin; - // If the itemremove is due to refreshing, ignore it. - // If the row for the current context record has gone after the - // refresh, editing will be canceled there. See onViewRefresh above. - if (!view.refreshing) { - plugin = me.editingPlugin; - grid = plugin.grid; - store = grid.getStore(); - gridView = me.editingPlugin.view; - context = this.context; - // Checking if this is a deleted record or an element being derendered - if (store.getById(me.getRecord().getId()) && !me._cachedNode) { - // if this is an items being derendered and is also being edited - // the flag _cachedNode will be set to true and an itemadd event will - // be added to monitor when the editor should be reactivated. - if (plugin.editing) { - this._cachedNode = true; - this.mon(gridView, { - itemadd: me.onViewItemAdd, - scope: me - }); - } - } else if (!me._cachedNode) { - this.activeField = null; - this.editingPlugin.cancelEdit(); - } - } - }, - onViewItemAdd: function(records, index, items, view) { - var me = this, - gridView, - plugin = me.editingPlugin; - // Checks if BufferedRenderer is adding the items - // if there was an item being edited, and it belongs to this batch - // then update the row and node associations. - if (me._cachedNode && plugin.editing) { - gridView = plugin.view; - // Checks if there is an array of records being added - // and if within this array, any record matches the one being edited before - // if it does, the editor context is updated, the itemadd - // event listener is removed and _cachedNode is cleared. - for (var i = 0; i < records.length; i++) { - if (records[i] === me.context.record) { - me.context.node = items[i]; - me.context.row = gridView.getRow(items[i]); - me.context.cell = gridView.getCellByPosition(me.context, true); - me.clearCache(); - break; - } - } - } - }, - onViewScroll: function() { - var me = this, - viewEl = me.editingPlugin.view.el, - scrollingView = me.scrollingView, - scrollTop = scrollingView.getScrollY(), - scrollLeft = scrollingView.getScrollX(), - scrollTopChanged = scrollTop !== me.lastScrollTop, - row; - me.lastScrollTop = scrollTop; - me.lastScrollLeft = scrollLeft; - if (me.isVisible()) { - row = Ext.getDom(me.context.row); - // Only reposition if the row is in the DOM (buffered rendering may mean the context row is not there) - if (row && viewEl.contains(row)) { - // This makes sure the Editor is repositioned if it was scrolled out of buffer range - if (me.getLocalY()) { - me.setLocalY(0); - } - if (scrollTopChanged) { - // The row element in the context may be stale due to buffered rendering removing out-of-view rows, then re-inserting newly rendered ones - me.context.row = row; - me.reposition(null, true); - if ((me.tooltip && me.tooltip.isVisible()) || me.hiddenTip) { - me.repositionTip(); - } - me.syncEditorClip(); - } - } else // If row is NOT in the DOM, ensure the editor is out of sight - { - me.setLocalY(-400); - } - } - }, - onColumnResize: function(column, width) { - var me = this; - if (me.rendered && !me.editingPlugin.reconfiguring) { - // Need to ensure our lockable/normal horizontal scrollrange is set - me.onGridResize(); - me.onViewScroll(); - if (!column.isGroupHeader) { - me.syncFieldWidth(column); - me.repositionIfVisible(); - } - } - }, - onColumnHide: function(column) { - if (!this.editingPlugin.reconfiguring && !column.isGroupHeader) { - column.getEditor().hide(); - this.repositionIfVisible(); - } - }, - onColumnShow: function(column) { - var me = this; - if (me.rendered && !me.editingPlugin.reconfiguring && !column.isGroupHeader && column.getEditor) { - column.getEditor().show(); - me.syncFieldWidth(column); - if (!me.preventReposition) { - this.repositionIfVisible(); - } - } - }, - onColumnMove: function(column, fromIdx, toIdx) { - var me = this, - locked = column.isLocked(), - fieldContainer = locked ? me.lockedColumnContainer : me.normalColumnContainer, - columns, i, len, after, offset; - // If moving a group, move each leaf header - if (column.isGroupHeader) { - Ext.suspendLayouts(); - after = toIdx > fromIdx; - offset = after ? 1 : 0; - columns = column.getGridColumns(); - for (i = 0 , len = columns.length; i < len; ++i) { - column = columns[i]; - toIdx = column.getIndex(); - if (after) { - ++offset; - } - me.setColumnEditor(column, toIdx + offset, fieldContainer); - } - Ext.resumeLayouts(true); - } else { - me.setColumnEditor(column, column.getIndex(), fieldContainer); - } - }, - setColumnEditor: function(column, idx, fieldContainer) { - this.addFieldsForColumn(column); - fieldContainer.insert(idx, column.getEditor()); - }, - onColumnAdd: function(column) { - // If a column header added, process its leaves - if (column.isGroupHeader) { - column = column.getGridColumns(); - } - //this.preventReposition = true; - this.addFieldsForColumn(column); - this.insertColumnEditor(column); - this.preventReposition = false; - }, - insertColumnEditor: function(column) { - var me = this, - field, fieldContainer, len, i; - if (Ext.isArray(column)) { - for (i = 0 , len = column.length; i < len; i++) { - me.insertColumnEditor(column[i]); - } - return; - } - if (!column.getEditor) { - return; - } - fieldContainer = column.isLocked() ? me.lockedColumnContainer : me.normalColumnContainer; - // Insert the column's field into the editor panel. - fieldContainer.insert(column.getIndex(), field = column.getEditor()); - // Ensure the view scrolls the field into view on focus - field.on('focus', me.onFieldFocus, me); - me.needsSyncFieldWidths = true; - }, - onFieldFocus: function(field) { - // Cache the active field so that we can restore focus into its cell onHide - // Makes the cursor always be placed at the end of the textfield - // when the field is being edited for the first time (IE only). - if (Ext.isIE) { - field.inputEl.dom.value = field.inputEl.dom.value; - } - this.activeField = field; - this.context.setColumn(field.column); - // skipFocusScroll should be true right after the editor has been started - if (!this.skipFocusScroll) { - field.column.getView().getScrollable().scrollIntoView(field.el); - } else { - this.skipFocusScroll = null; - } - }, - onFieldTab: function(e) { - var me = this, - activeField = me.activeField, - rowIdx = me.context.rowIdx, - forwards = !e.shiftKey, - target = activeField[forwards ? 'nextNode' : 'previousNode'](':focusable'); - // No field to TAB to, navigate forwards or backwards - if (!target || !target.isDescendant(me)) { - // Tabbing out of a dirty editor - wrap to the update button - if (me.isDirty()) { - e.preventDefault(); - me.floatingButtons.child('#update').focus(); - } else { - // Editor is clean - navigate to next or previous row - rowIdx = rowIdx + (forwards ? 1 : -1); - if (rowIdx >= 0 && rowIdx <= me.view.dataSource.getCount()) { - if (forwards) { - target = me.down(':focusable:not([isButton]):first'); - // If going back to the first column, scroll back to field. - // If we're in a locking view, this has to be done programatically to avoid jarring - // when navigating from the locked back into the normal side - activeField.column.getView().getScrollable().scrollIntoView(activeField.ownerCt.child(':focusable').el); - } else { - target = me.down(':focusable:not([isButton]):last'); - } - me.editingPlugin.startEdit(rowIdx, target.column); - } - } - } - }, - destroyColumnEditor: function(column) { - var field; - if (column.hasEditor() && (field = column.getEditor())) { - field.destroy(); - } - }, - getFloatingButtons: function() { - var me = this, - btns = me.floatingButtons; - if (!btns) { - me.floatingButtons = btns = new Ext.grid.RowEditorButtons({ - ownerCmp: me, - rowEditor: me - }); - } - return btns; - }, - repositionIfVisible: function(c) { - var me = this, - view = me.view; - // If we're showing ourselves, jump out - // If the component we're showing doesn't contain the view - if (c && (c === me || !c.el.isAncestor(view.el))) { - return; - } - if (me.isVisible() && view.isVisible(true)) { - me.reposition(); - } - }, - isLayoutChild: function(ownerCandidate) { - // RowEditor is not a floating component, but won't be laid out by the grid - return false; - }, - getRefOwner: function() { - return this.editingPlugin.grid; - }, - getRefItems: function(deep) { - var me = this, - result; - if (me.lockable) { - // refItems must include ALL children. Must include the two containers - // because we don't know what is being searched for. - result = [ - me.lockedColumnContainer - ]; - result.push.apply(result, me.lockedColumnContainer.getRefItems(deep)); - result.push(me.normalColumnContainer); - result.push.apply(result, me.normalColumnContainer.getRefItems(deep)); - } else { - result = me.callParent(arguments); - } - result.push.apply(result, me.getFloatingButtons().getRefItems(deep)); - return result; - }, - reposition: function(animateConfig, fromScrollHandler) { - var me = this, - context = me.context, - row = context && context.row, - wrapEl = me.wrapEl, - rowTop, localY, deltaY, afterPosition; - // Position this editor if the context row is rendered (buffered rendering may mean that it's not in the DOM at all) - if (row && Ext.isElement(row)) { - deltaY = me.syncButtonPosition(me.getScrollDelta()); - rowTop = me.calculateLocalRowTop(row); - localY = me.calculateEditorTop(rowTop); - // If not being called from scroll handler... - // If the editor's top will end up above the fold - // or the bottom will end up below the fold, - // organize an afterPosition handler which will bring it into view and focus the correct input field - if (!fromScrollHandler) { - afterPosition = function() { - if (deltaY) { - me.scrollingViewEl.scrollBy(0, deltaY, true); - } - me.focusColumnField(context.column); - }; - } - me.syncEditorClip(); - // Get the y position of the row relative to its top-most static parent. - // offsetTop will be relative to the table, and is incorrect - // when mixed with certain grid features (e.g., grouping). - if (animateConfig) { - wrapEl.animate(Ext.applyIf({ - to: { - top: localY - }, - duration: animateConfig.duration || 125, - callback: afterPosition - }, animateConfig)); - } else { - wrapEl.setLocalY(localY); - if (afterPosition) { - afterPosition(); - } - } - } - }, - /** - * @private - * Returns the scroll delta required to scroll the context row into view in order to make - * the whole of this editor visible. - * @return {Number} the scroll delta. Zero if scrolling is not required. - */ - getScrollDelta: function() { - var me = this, - scrollingViewDom = me.scrollingViewEl.dom, - context = me.context, - body = me.body, - deltaY = 0; - if (context) { - deltaY = Ext.fly(context.row).getOffsetsTo(scrollingViewDom)[1]; - if (deltaY < 0) { - deltaY -= body.getBorderPadding().beforeY; - } else if (deltaY > 0) { - deltaY = Math.max(deltaY + me.getHeight() + me.floatingButtons.getHeight() - scrollingViewDom.clientHeight - body.getBorderWidth('b'), 0); - if (deltaY > 0) { - deltaY -= body.getBorderPadding().afterY; - } - } - } - return deltaY; - }, - // - // Calculates the top pixel position of the passed row within the view's scroll space. - // So in a large, scrolled grid, this could be several thousand pixels. - // - calculateLocalRowTop: function(row) { - var grid = this.editingPlugin.grid; - return Ext.fly(row).getOffsetsTo(grid)[1] - grid.el.getBorderWidth('t') + this.lastScrollTop; - }, - // Given the top pixel position of a row in the scroll space, - // calculate the editor top position in the view's encapsulating element. - // This will only ever be in the visible range of the view's element. - calculateEditorTop: function(rowTop) { - return rowTop - this.body.getBorderPadding().beforeY - this.lastScrollTop; - }, - getClientWidth: function() { - var me = this, - grid = me.editingPlugin.grid, - result; - if (me.lockable) { - result = grid.lockedGrid.getWidth() + grid.normalGrid.view.el.dom.clientWidth; - } else { - result = grid.view.el.dom.clientWidth; - } - return result; - }, - getEditor: function(fieldInfo) { - var me = this; - if (Ext.isNumber(fieldInfo)) { - // In a locked grid, a RowEditor uses 2 inner containers, so need to use CQ to retrieve - // configured editors which were stamped with the isEditorComponent property in Editing.createColumnField - return me.query('[isEditorComponent]')[fieldInfo]; - } else if (fieldInfo.isHeader && !fieldInfo.isGroupHeader) { - return fieldInfo.getEditor(); - } - }, - addFieldsForColumn: function(column, initial) { - var me = this, - i, length, field; - if (Ext.isArray(column)) { - for (i = 0 , length = column.length; i < length; i++) { - me.addFieldsForColumn(column[i], initial); - } - return; - } - if (column.getEditor) { - // Get a default display field if necessary - field = column.getEditor(null, me.getDefaultFieldCfg()); - if (column.align === 'right') { - field.fieldStyle = 'text-align:right'; - } - if (column.xtype === 'actioncolumn') { - field.fieldCls += ' ' + Ext.baseCSSPrefix + 'form-action-col-field'; - } - if (me.isVisible() && me.context) { - if (field.is('displayfield')) { - me.renderColumnData(field, me.context.record, column); - } else { - field.suspendEvents(); - field.setValue(me.context.record.get(column.dataIndex)); - field.resumeEvents(); - } - } - if (column.hidden) { - me.onColumnHide(column); - } else if (column.rendered && !initial) { - // Setting after initial render - me.onColumnShow(column); - } - } - }, - getDefaultFieldCfg: function() { - return { - xtype: 'displayfield', - // Override Field's implementation so that the default display fields will not return values. This is done because - // the display field will pick up column renderers from the grid. - getModelData: function() { - return null; - } - }; - }, - loadRecord: function(record) { - var me = this, - form = me.getForm(), - fields = form.getFields(), - items = fields.items, - length = items.length, - i, displayFields, isValid, item; - // temporarily suspend events on form fields before loading record to prevent the fields' change events from firing - for (i = 0; i < length; i++) { - item = items[i]; - item.suspendEvents(); - item.resetToInitialValue(); - } - form.loadRecord(record); - for (i = 0; i < length; i++) { - items[i].resumeEvents(); - } - // Because we suspend the events, none of the field events will get propagated to - // the form, so the valid state won't be correct. - if (form.hasInvalidField() === form.wasValid) { - delete form.wasValid; - } - isValid = form.isValid(); - if (me.errorSummary) { - if (isValid) { - me.hideToolTip(); - } else { - me.showToolTip(); - } - } - me.updateButton(isValid); - // render display fields so they honor the column renderer/template - displayFields = me.query('>displayfield'); - length = displayFields.length; - for (i = 0; i < length; i++) { - me.renderColumnData(displayFields[i], record); - } - }, - renderColumnData: function(field, record, activeColumn) { - var me = this, - grid = me.editingPlugin.grid, - headerCt = grid.headerCt, - view = me.scrollingView, - store = view.dataSource, - column = activeColumn || field.column, - value = record.get(column.dataIndex), - renderer = column.editRenderer || column.renderer, - metaData, rowIdx, colIdx, - scope = (column.usingDefaultRenderer && !column.scope) ? column : column.scope; - // honor our column's renderer (TemplateHeader sets renderer for us!) - if (renderer) { - metaData = { - tdCls: '', - style: '' - }; - rowIdx = store.indexOf(record); - colIdx = headerCt.getHeaderIndex(column); - value = renderer.call(scope || headerCt.ownerCt, value, metaData, record, rowIdx, colIdx, store, view); - } - field.setRawValue(value); - }, - beforeEdit: function() { - var me = this, - scrollDelta; - if (me.isVisible() && me.errorSummary && !me.autoCancel && me.isDirty()) { - // Scroll the visible RowEditor that is in error state back into view - scrollDelta = me.getScrollDelta(); - if (scrollDelta) { - me.scrollingViewEl.scrollBy(0, scrollDelta, true); - } - me.showToolTip(); - return false; - } - }, - /** - * Start editing the specified grid at the specified position. - * @param {Ext.data.Model} record The Store data record which backs the row to be edited. - * @param {Ext.data.Model} columnHeader The Column object defining the column to be focused - */ - startEdit: function(record, columnHeader) { - var me = this, - editingPlugin = me.editingPlugin, - grid = editingPlugin.grid, - context = me.context = editingPlugin.context, - alreadyVisible = me.isVisible(), - wrapEl = me.wrapEl; - if (me._cachedNode) { - me.clearCache(); - } - // Ensure that the render operation does not lay out - // The show call will update the layout - Ext.suspendLayouts(); - if (!me.rendered) { - me.width = me.getClientWidth(); - me.render(grid.el, grid.el.dom.firstChild); - // The wrapEl is a container for the editor and buttons. We use a wrap el - // (instead of rendering the buttons inside the editor) so that the editor and - // buttons can be clipped separately when overflowing. - // See https://sencha.jira.com/browse/EXTJS-13851 - wrapEl = me.wrapEl = me.el.wrap(); - // Change the visibilityMode to offsets so that we get accurate measurements - // when the roweditor is hidden for laying out things like a TriggerField. - wrapEl.setVisibilityMode(3); - wrapEl.addCls(me._wrapCls); - me.getFloatingButtons().render(wrapEl); - // On first show we need to ensure that we have the scroll positions cached - me.onViewScroll(); - } - me.setLocalY(0); - // Select at the clicked position. - context.grid.getSelectionModel().selectByPosition({ - row: record, - column: columnHeader - }); - // Make sure the container el is correctly sized. - me.onGridResize(); - // Reload the record data - me.loadRecord(record); - // Layout the form with the new content if we are already visible. - // Otherwise, just allow resumption, and the show will update the layout. - Ext.resumeLayouts(alreadyVisible); - if (alreadyVisible) { - me.reposition(true); - } else { - // this will prevent the onFieldFocus method from calling - // scrollIntoView right after startEdit as this will be - // handled by the Editing plugin. - me.skipFocusScroll = true; - me.show(); - } - }, - // determines the amount by which the row editor will overflow, and flips the buttons - // to the top of the editor if the required scroll amount is greater than the available - // scroll space. Returns the scrollDelta required to scroll the editor into view after - // adjusting the button position. - syncButtonPosition: function(scrollDelta) { - var me = this, - floatingButtons = me.getFloatingButtons(), - scrollingView = me.scrollingView, - overflow = me.getScrollDelta() - (scrollingView.getScrollable().getSize().y - scrollingView.getScrollY() - me.scrollingViewEl.dom.clientHeight); - if (overflow > 0) { - if (!me._buttonsOnTop) { - floatingButtons.setButtonPosition('top'); - me._buttonsOnTop = true; - } - scrollDelta = 0; - } else if (me._buttonsOnTop !== false) { - floatingButtons.setButtonPosition('bottom'); - me._buttonsOnTop = false; - } else // Ensure button Y position is synced with Editor height even if button - // orientation doesn't change - { - floatingButtons.setButtonPosition(floatingButtons.position); - } - return scrollDelta; - }, - // since the editor is rendered to the grid el, it must be clipped when scrolled - // outside of the grid view area so that it does not overlap the scrollbar or docked items - // Since safari's clip implementation does not accept negative values we cannot clip - // both buttons and editor by setting clip on a single element, because it will result - // in the buttons being hidden when they are positioned above the editor. - // See https://sencha.jira.com/browse/EXTJS-13851 - // To work around this we render the buttons and editor to a wrapping element and clip - // them separately. - syncEditorClip: function() { - var me = this, - overflow = me.getScrollDelta(), - el = me.el, - floatingButtons = me.floatingButtons, - btnEl = floatingButtons.el, - max = Math.max, - body, btnHeight, editorHeight; - if (overflow) { - // The editor is overflowing outside of the view area, either above or below - me.isOverflowing = true; - body = me.body; - btnHeight = floatingButtons.getHeight(); - editorHeight = me.getHeight(); - max = Math.max; - if (overflow > 0) { - // editor is overflowing the bottom of the view - if (me._buttonsOnTop) { - overflow -= (btnHeight - body.getBorderWidth('b')); - me.clipBottom(el, max(editorHeight - overflow), 0); - overflow -= (editorHeight - body.getBorderWidth('t')); - if (overflow > 0) { - me.clipBottom(btnEl, max(btnHeight - overflow, 0)); - } else { - me.clearClip(btnEl); - } - } else { - me.clipBottom(btnEl, max(btnHeight - overflow, 0)); - overflow -= (btnHeight - body.getBorderWidth('b')); - if (overflow > 0) { - me.clipBottom(el, max(editorHeight - overflow, 0)); - } else { - me.clearClip(el); - } - } - } else if (overflow < 0) { - // editor is overflowing the top of the view - overflow = Math.abs(overflow); - me.clipTop(el, overflow); - overflow -= (editorHeight - body.getBorderWidth('b')); - if (overflow > 0) { - me.clipTop(btnEl, overflow); - } else { - me.clearClip(btnEl); - } - } - } else if (me.isOverflowing) { - me.clearClip(btnEl); - me.clearClip(el); - me.isOverflowing = false; - } - }, - focusColumnField: function(column) { - var field, didFocus; - if (column && !column.destroyed) { - if (column.isVisible()) { - field = this.getEditor(column); - if (field && field.isFocusable(true)) { - didFocus = true; - field.focus(); - } - } - if (!didFocus) { - this.focusColumnField(column.next()); - } - } - }, - cancelEdit: function() { - var me = this, - form = me.getForm(), - fields = form.getFields(), - items = fields.items, - length = items.length, - i; - if (me._cachedNode) { - me.clearCache(); - } - me.hide(); - form.clearInvalid(); - // temporarily suspend events on form fields before reseting the form to prevent the fields' change events from firing - for (i = 0; i < length; i++) { - items[i].suspendEvents(); - } - form.reset(); - for (i = 0; i < length; i++) { - items[i].resumeEvents(); - } - }, - /* - * @private - */ - clearCache: function() { - var me = this; - me.mun(me.editingPlugin.view, { - itemadd: me.onViewItemAdd, - scope: me - }); - me._cachedNode = false; - }, - completeEdit: function() { - var me = this, - form = me.getForm(); - if (!form.isValid()) { - return false; - } - me.completing = true; - form.updateRecord(me.context.record); - me.hide(); - me.completing = false; - return true; - }, - onShow: function() { - var me = this; - me.wrapEl.show(); - me.callParent(arguments); - if (me.needsSyncFieldWidths) { - me.suspendLayouts(); - me.syncAllFieldWidths(); - me.resumeLayouts(true); - } - delete me.needsSyncFieldWidths; - me.reposition(); - }, - onHide: function() { - var me = this, - context = me.context, - column, focusContext, - activeEl = Ext.Element.getActiveElement(); - // If they used ESC or ENTER in a Field - if (me.el.contains(activeEl)) { - column = me.activeField.column; - } else // If they used a button - { - column = context.column; - } - focusContext = new Ext.grid.CellContext(column.getView()).setPosition(me.context.record, column); - focusContext.view.getNavigationModel().setPosition(focusContext); - me.activeField = null; - me.wrapEl.hide(); - me.callParent(arguments); - if (me.tooltip) { - me.hideToolTip(); - } - }, - onResize: function(width, height) { - this.wrapEl.setSize(width, height); - }, - isDirty: function() { - return this.getForm().isDirty(); - }, - getToolTip: function() { - var me = this, - tip = me.tooltip, - grid = me.editingPlugin.grid; - if (!tip) { - me.tooltip = tip = new Ext.tip.ToolTip({ - cls: Ext.baseCSSPrefix + 'grid-row-editor-errors', - title: me.errorsText, - autoHide: false, - closable: true, - closeAction: 'disable', - anchor: 'left', - anchorToTarget: true, - constrainPosition: true, - constrainTo: document.body - }); - grid.add(tip); - // Layout may change the grid's positioning. - me.mon(grid, { - afterlayout: me.onGridLayout, - scope: me - }); - } - return tip; - }, - hideToolTip: function() { - var me = this, - tip = me.getToolTip(); - if (tip.rendered) { - tip.disable(); - } - me.hiddenTip = false; - }, - showToolTip: function() { - var me = this, - tip = me.getToolTip(); - tip.update(me.getErrors()); - me.repositionTip(); - tip.enable(); - }, - onGridLayout: function() { - if (this.tooltip && this.tooltip.isVisible()) { - this.repositionTip(); - } - }, - repositionTip: function() { - var me = this, - tip = me.getToolTip(), - context = me.context, - row = Ext.get(context.row), - viewEl = me.scrollingViewEl, - viewHeight = viewEl.dom.clientHeight, - viewTop = viewEl.getY(), - viewBottom = viewTop + viewHeight, - rowHeight = row.getHeight(), - rowTop = row.getY(), - rowBottom = rowTop + rowHeight; - if (rowBottom > viewTop && rowTop < viewBottom) { - // Use the ToolTip's anchoring to get the left/right positioning correct with - // respect to space available on the default (right) side. - tip.anchorTarget = viewEl; - tip.mouseOffset = [ - 0, - row.getOffsetsTo(viewEl)[1] - ]; - // The tip will realign itself based upon its new offset - tip.show(); - me.hiddenTip = false; - } else { - tip.hide(); - me.hiddenTip = true; - } - }, - getErrors: function() { - var me = this, - errors = [], - fields = me.query('>[isFormField]'), - length = fields.length, - i, fieldErrors, field; - for (i = 0; i < length; i++) { - field = fields[i]; - fieldErrors = field.getErrors(); - if (fieldErrors.length) { - errors.push(me.createErrorListItem(fieldErrors[0], field.column.text)); - } - } - // Only complain about unsaved changes if all the fields are valid - if (!errors.length && !me.autoCancel && me.isDirty()) { - errors[0] = me.createErrorListItem(me.dirtyText); - } - return '
      ' + errors.join('') + '
    '; - }, - createErrorListItem: function(e, name) { - e = name ? name + ': ' + e : e; - return '
  • ' + e + '
  • '; - }, - beforeDestroy: function() { - Ext.destroy(this.floatingButtons, this.tooltip); - this.callParent(); - }, - clipBottom: function(el, value) { - el.setStyle('clip', 'rect(0 auto ' + value + 'px 0)'); - }, - clipTop: function(el, value) { - el.setStyle('clip', 'rect(' + value + 'px, auto, auto, 0)'); - }, - clearClip: function(el) { - el.setStyle('clip', Ext.isIE8 ? 'rect(-1000px auto 1000px auto)' : 'auto'); - } -}); - -Ext.define('Ext.grid.Scroller', { - constructor: Ext.deprecated() -}); - -/** - * @private - */ -Ext.define('Ext.view.DropZone', { - extend: 'Ext.dd.DropZone', - indicatorCls: Ext.baseCSSPrefix + 'grid-drop-indicator', - indicatorHtml: [ - '', - '' - ].join(''), - constructor: function(config) { - var me = this; - Ext.apply(me, config); - // Create a ddGroup unless one has been configured. - // User configuration of ddGroups allows users to specify which - // DD instances can interact with each other. Using one - // based on the id of the View would isolate it and mean it can only - // interact with a DragZone on the same View also using a generated ID. - if (!me.ddGroup) { - me.ddGroup = 'view-dd-zone-' + me.view.id; - } - // The DropZone's encapsulating element is the View's main element. It must be this because drop gestures - // may require scrolling on hover near a scrolling boundary. In Ext 4.x two DD instances may not use the - // same element, so a DragZone on this same View must use the View's parent element as its element. - me.callParent([ - me.view.el - ]); - }, - // Fire an event through the client DataView. Lock this DropZone during the event processing so that - // its data does not become corrupted by processing mouse events. - fireViewEvent: function() { - var me = this, - result; - me.lock(); - result = me.view.fireEvent.apply(me.view, arguments); - me.unlock(); - return result; - }, - getTargetFromEvent: function(e) { - var node = e.getTarget(this.view.getItemSelector()), - mouseY, nodeList, testNode, i, len, box; - // Not over a row node: The content may be narrower than the View's encapsulating element, so return the closest. - // If we fall through because the mouse is below the nodes (or there are no nodes), we'll get an onContainerOver call. - if (!node) { - mouseY = e.getY(); - for (i = 0 , nodeList = this.view.getNodes() , len = nodeList.length; i < len; i++) { - testNode = nodeList[i]; - box = Ext.fly(testNode).getBox(); - if (mouseY <= box.bottom) { - return testNode; - } - } - } - return node; - }, - getIndicator: function() { - var me = this; - if (!me.indicator) { - me.indicator = new Ext.Component({ - ariaRole: 'presentation', - html: me.indicatorHtml, - cls: me.indicatorCls, - ownerCt: me.view, - floating: true, - shadow: false - }); - } - return me.indicator; - }, - getPosition: function(e, node) { - var y = e.getXY()[1], - region = Ext.fly(node).getRegion(), - pos; - if ((region.bottom - y) >= (region.bottom - region.top) / 2) { - pos = "before"; - } else { - pos = "after"; - } - return pos; - }, - /** - * @private - * Determines whether the record at the specified offset from the passed record - * is in the drag payload. - * @param records - * @param record - * @param offset - * @return {Boolean} True if the targeted record is in the drag payload - */ - containsRecordAtOffset: function(records, record, offset) { - if (!record) { - return false; - } - var view = this.view, - recordIndex = view.indexOf(record), - nodeBefore = view.getNode(recordIndex + offset), - recordBefore = nodeBefore ? view.getRecord(nodeBefore) : null; - return recordBefore && Ext.Array.contains(records, recordBefore); - }, - positionIndicator: function(node, data, e) { - var me = this, - view = me.view, - pos = me.getPosition(e, node), - overRecord = view.getRecord(node), - draggingRecords = data.records, - indicatorY; - if (!Ext.Array.contains(draggingRecords, overRecord) && (pos === 'before' && !me.containsRecordAtOffset(draggingRecords, overRecord, -1) || pos === 'after' && !me.containsRecordAtOffset(draggingRecords, overRecord, 1))) { - me.valid = true; - if (me.overRecord !== overRecord || me.currentPosition !== pos) { - indicatorY = Ext.fly(node).getY() - view.el.getY() - 1; - if (pos === 'after') { - indicatorY += Ext.fly(node).getHeight(); - } - // If view is scrolled using CSS translate, account for then when positioning the indicator - if (view.touchScroll === 2) { - indicatorY += view.getScrollY(); - } - me.getIndicator().setWidth(Ext.fly(view.el).getWidth()).showAt(0, indicatorY); - // Cache the overRecord and the 'before' or 'after' indicator. - me.overRecord = overRecord; - me.currentPosition = pos; - } - } else { - me.invalidateDrop(); - } - }, - invalidateDrop: function() { - if (this.valid) { - this.valid = false; - this.getIndicator().hide(); - } - }, - // The mouse is over a View node - onNodeOver: function(node, dragZone, e, data) { - var me = this; - if (!Ext.Array.contains(data.records, me.view.getRecord(node))) { - me.positionIndicator(node, data, e); - } - return me.valid ? me.dropAllowed : me.dropNotAllowed; - }, - // Moved out of the DropZone without dropping. - // Remove drop position indicator - notifyOut: function(node, dragZone, e, data) { - var me = this; - me.callParent(arguments); - me.overRecord = me.currentPosition = null; - me.valid = false; - if (me.indicator) { - me.indicator.hide(); - } - }, - // The mouse is past the end of all nodes (or there are no nodes) - onContainerOver: function(dd, e, data) { - var me = this, - view = me.view, - count = view.dataSource.getCount(); - // There are records, so position after the last one - if (count) { - me.positionIndicator(view.all.last(), data, e); - } else // No records, position the indicator at the top - { - me.overRecord = me.currentPosition = null; - me.getIndicator().setWidth(Ext.fly(view.el).getWidth()).showAt(0, 0); - me.valid = true; - } - return me.dropAllowed; - }, - onContainerDrop: function(dd, e, data) { - return this.onNodeDrop(dd, null, e, data); - }, - onNodeDrop: function(targetNode, dragZone, e, data) { - var me = this, - dropHandled = false, - // Create a closure to perform the operation which the event handler may use. - // Users may now set the wait parameter in the beforedrop handler, and perform any kind - // of asynchronous processing such as an Ext.Msg.confirm, or an Ajax request, - // and complete the drop gesture at some point in the future by calling either the - // processDrop or cancelDrop methods. - dropHandlers = { - wait: false, - processDrop: function() { - me.invalidateDrop(); - me.handleNodeDrop(data, me.overRecord, me.currentPosition); - dropHandled = true; - me.fireViewEvent('drop', targetNode, data, me.overRecord, me.currentPosition); - }, - cancelDrop: function() { - me.invalidateDrop(); - dropHandled = true; - } - }, - performOperation = false; - if (me.valid) { - performOperation = me.fireViewEvent('beforedrop', targetNode, data, me.overRecord, me.currentPosition, dropHandlers); - if (dropHandlers.wait) { - return; - } - if (performOperation !== false) { - // If either of the drop handlers were called in the event handler, do not do it again. - if (!dropHandled) { - dropHandlers.processDrop(); - } - } - } - return performOperation; - }, - destroy: function() { - this.indicator = Ext.destroy(this.indicator); - this.callParent(); - } -}); - -/** - * @private - */ -Ext.define('Ext.grid.ViewDropZone', { - extend: 'Ext.view.DropZone', - indicatorHtml: '', - indicatorCls: Ext.baseCSSPrefix + 'grid-drop-indicator', - handleNodeDrop: function(data, record, position) { - var view = this.view, - store = view.getStore(), - index, records, i, len; - // If the copy flag is set, create a copy of the models - if (data.copy) { - records = data.records; - data.records = []; - for (i = 0 , len = records.length; i < len; i++) { - data.records.push(records[i].copy()); - } - } else { - /* - * Remove from the source store. We do this regardless of whether the store - * is the same bacsue the store currently doesn't handle moving records - * within the store. In the future it should be possible to do this. - * Here was pass the isMove parameter if we're moving to the same view. - */ - data.view.store.remove(data.records, data.view === view); - } - if (record && position) { - index = store.indexOf(record); - // 'after', or undefined (meaning a drop at index -1 on an empty View)... - if (position !== 'before') { - index++; - } - store.insert(index, data.records); - } else // No position specified - append. - { - store.add(data.records); - } - // Select the dropped nodes - view.getSelectionModel().select(data.records); - // Focus the first dropped node. - view.getNavigationModel().setPosition(data.records[0]); - } -}); - -/** - * Plugin to add header resizing functionality to a HeaderContainer. - * Always resizing header to the left of the splitter you are resizing. - */ -Ext.define('Ext.grid.plugin.HeaderResizer', { - extend: 'Ext.plugin.Abstract', - requires: [ - 'Ext.dd.DragTracker', - 'Ext.util.Region' - ], - alias: 'plugin.gridheaderresizer', - disabled: false, - config: { - /** - * @cfg {Boolean} dynamic - * True to resize on the fly rather than using a proxy marker. - * @accessor - */ - dynamic: false - }, - colHeaderCls: Ext.baseCSSPrefix + 'column-header', - minColWidth: 40, - maxColWidth: 1000, - eResizeCursor: 'col-resize', - init: function(headerCt) { - var me = this; - me.headerCt = headerCt; - headerCt.on('render', me.afterHeaderRender, me, { - single: me - }); - // Pull minColWidth from the minWidth in the Column prototype - if (!me.minColWidth) { - me.self.prototype.minColWidth = Ext.grid.column.Column.prototype.minWidth; - } - }, - destroy: function() { - var me = this, - tracker = me.tracker; - if (tracker) { - tracker.destroy(); - me.tracker = null; - } - // The grid may happen to never render - me.headerCt.un('render', me.afterHeaderRender, me); - me.headerCt = null; - me.callParent(); - }, - afterHeaderRender: function() { - var me = this, - headerCt = me.headerCt, - el = headerCt.el; - headerCt.mon(el, 'mousemove', me.onHeaderCtMouseMove, me); - me.markerOwner = me.ownerGrid = me.headerCt.up('tablepanel').ownerGrid; - me.tracker = new Ext.dd.DragTracker({ - disabled: me.disabled, - onBeforeStart: me.onBeforeStart.bind(me), - onStart: me.onStart.bind(me), - onDrag: me.onDrag.bind(me), - onEnd: me.onEnd.bind(me), - tolerance: 3, - autoStart: 300, - el: el - }); - }, - // As we mouse over individual headers, change the cursor to indicate - // that resizing is available, and cache the resize target header for use - // if/when they mousedown. - onHeaderCtMouseMove: function(e) { - var me = this; - if (me.headerCt.dragging || me.disabled) { - if (me.activeHd) { - me.activeHd.el.dom.style.cursor = ''; - delete me.activeHd; - } - } else if (e.pointerType !== 'touch') { - me.findActiveHeader(e); - } - }, - findActiveHeader: function(e) { - var me = this, - headerCt = me.headerCt, - headerEl = e.getTarget('.' + me.colHeaderCls, headerCt.el, true), - ownerGrid = me.ownerGrid, - ownerLockable = ownerGrid.ownerLockable, - overHeader, resizeHeader, headers, header; - me.activeHd = null; - if (headerEl) { - overHeader = Ext.getCmp(headerEl.id); - // If near the right edge, we're resizing the column we are over. - if (overHeader.isAtEndEdge(e)) { - // Cannot resize the only column in a forceFit grid. - if (headerCt.visibleColumnManager.getColumns().length === 1 && headerCt.forceFit) { - return; - } - resizeHeader = overHeader; - } - // Else... we might be near the right edge - else if (overHeader.isAtStartEdge(e)) { - // Extract previous visible leaf header - headers = headerCt.visibleColumnManager.getColumns(); - header = overHeader.isGroupHeader ? overHeader.getGridColumns()[0] : overHeader; - resizeHeader = headers[Ext.Array.indexOf(headers, header) - 1]; - // If there wasn't one, and we are the normal side of a lockable assembly then - // use the last visible leaf header of the locked side. - if (!resizeHeader && ownerLockable && !ownerGrid.isLocked) { - headers = ownerLockable.lockedGrid.headerCt.visibleColumnManager.getColumns(); - resizeHeader = headers[headers.length - 1]; - } - } - // We *are* resizing - if (resizeHeader) { - // If we're attempting to resize a group header, that cannot be resized, - // so find its last visible leaf header; Group headers are sized - // by the size of their child headers. - if (resizeHeader.isGroupHeader) { - headers = resizeHeader.getGridColumns(); - resizeHeader = headers[headers.length - 1]; - } - // Check if the header is resizable. Continue checking the old "fixed" property, bug also - // check whether the resizable property is set to false. - if (resizeHeader && !(resizeHeader.fixed || (resizeHeader.resizable === false))) { - me.activeHd = resizeHeader; - overHeader.el.dom.style.cursor = me.eResizeCursor; - if (overHeader.triggerEl) { - overHeader.triggerEl.dom.style.cursor = me.eResizeCursor; - } - } - } else // reset - { - overHeader.el.dom.style.cursor = ''; - if (overHeader.triggerEl) { - overHeader.triggerEl.dom.style.cursor = ''; - } - } - } - return me.activeHd; - }, - // only start when there is an activeHd - onBeforeStart: function(e) { - var me = this; - // If on touch, we will have received no mouseover, so we have to - // decide whether the touchstart is in a resize zone, and if so, which header is to be sized. - // Cache any activeHd because it will be cleared on subsequent mousemoves outside the resize zone. - me.dragHd = me.activeHd || e.pointerType === 'touch' && me.findActiveHeader(e); - if (me.dragHd && !me.headerCt.dragging) { - // Calculate how far off the right marker line the mouse pointer is. - // This will be the xDelta during the following drag operation. - me.xDelta = me.dragHd.getX() + me.dragHd.getWidth() - me.tracker.getXY()[0]; - me.tracker.constrainTo = me.getConstrainRegion(); - return true; - } else { - me.headerCt.dragging = false; - return false; - } - }, - // get the region to constrain to, takes into account max and min col widths - getConstrainRegion: function() { - var me = this, - dragHdEl = me.dragHd.el, - nextHd, - ownerGrid = me.ownerGrid, - widthModel = ownerGrid.getSizeModel().width, - maxColWidth = widthModel.shrinkWrap ? me.headerCt.getWidth() - me.headerCt.visibleColumnManager.getColumns().length * me.minColWidth : me.maxColWidth, - result; - // If forceFit, then right constraint is based upon not being able to force the next header - // beyond the minColWidth. If there is no next header, then the header may not be expanded. - if (me.headerCt.forceFit) { - nextHd = me.dragHd.nextNode('gridcolumn:not([hidden]):not([isGroupHeader])'); - if (nextHd && me.headerInSameGrid(nextHd)) { - maxColWidth = dragHdEl.getWidth() + (nextHd.getWidth() - me.minColWidth); - } - } - // If resize header is in a locked grid, the maxWidth has to be 30px within the available locking grid's width - // But only if the locked grid shrinkwraps its columns - else if (ownerGrid.isLocked && widthModel.shrinkWrap) { - maxColWidth = me.dragHd.up('[scrollerOwner]').getTargetEl().getWidth(true) - ownerGrid.getWidth() - (ownerGrid.ownerLockable.normalGrid.visibleColumnManager.getColumns().length * me.minColWidth + Ext.getScrollbarSize().width); - } - result = me.adjustConstrainRegion(dragHdEl.getRegion(), 0, 0, 0, me.minColWidth); - result.right = dragHdEl.getX() + maxColWidth; - return result; - }, - // initialize the left and right hand side markers around - // the header that we are resizing - onStart: function(e) { - var me = this, - dragHd = me.dragHd, - width = dragHd.el.getWidth(), - headerCt = dragHd.getRootHeaderCt(), - x, y, markerOwner, lhsMarker, rhsMarker, markerHeight; - me.headerCt.dragging = true; - me.origWidth = width; - // setup marker proxies - if (!me.dynamic) { - markerOwner = me.markerOwner; - // https://sencha.jira.com/browse/EXTJSIV-11299 - // In Neptune (and other themes with wide frame borders), resize handles are embedded in borders, - // *outside* of the outer element's content area, therefore the outer element is set to overflow:visible. - // During column resize, we should not see the resize markers outside the grid, so set to overflow:hidden. - if (markerOwner.frame && markerOwner.resizable) { - me.gridOverflowSetting = markerOwner.el.dom.style.overflow; - markerOwner.el.dom.style.overflow = 'hidden'; - } - x = me.getLeftMarkerX(markerOwner); - lhsMarker = markerOwner.getLhsMarker(); - rhsMarker = markerOwner.getRhsMarker(); - markerHeight = me.ownerGrid.body.getHeight() + headerCt.getHeight(); - y = headerCt.getOffsetsTo(markerOwner)[1] - markerOwner.el.getBorderWidth('t'); - // Ensure the markers have the correct cursor in case the cursor is *exactly* over - // this single pixel line, not just within the active resize zone - lhsMarker.dom.style.cursor = me.eResizeCursor; - rhsMarker.dom.style.cursor = me.eResizeCursor; - lhsMarker.setLocalY(y); - rhsMarker.setLocalY(y); - lhsMarker.setHeight(markerHeight); - rhsMarker.setHeight(markerHeight); - me.setMarkerX(lhsMarker, x); - me.setMarkerX(rhsMarker, x + width); - } - }, - // synchronize the rhsMarker with the mouse movement - onDrag: function(e) { - var me = this; - if (me.dynamic) { - me.doResize(); - } else { - me.setMarkerX(me.getMovingMarker(me.markerOwner), me.calculateDragX(me.markerOwner)); - } - }, - getMovingMarker: function(markerOwner) { - return markerOwner.getRhsMarker(); - }, - onEnd: function(e) { - var me = this, - markerOwner = me.markerOwner; - me.headerCt.dragging = false; - if (me.dragHd) { - if (!me.dynamic) { - // If we had saved the gridOverflowSetting, restore it - if ('gridOverflowSetting' in me) { - markerOwner.el.dom.style.overflow = me.gridOverflowSetting; - } - // hide markers - me.setMarkerX(markerOwner.getLhsMarker(), -9999); - me.setMarkerX(markerOwner.getRhsMarker(), -9999); - } - me.doResize(); - // On mouseup (a real mouseup), we must be ready to start dragging again immediately - - // Leave the activeHd active. - if (e.pointerType !== 'touch') { - me.dragHd = null; - me.activeHd.el.dom.style.cursor = me.eResizeCursor; - } else { - me.dragHd = me.activeHd = null; - } - } - // Do not process the upcoming click after this mouseup. It's not a click gesture - me.headerCt.blockNextEvent(); - }, - doResize: function() { - var me = this, - dragHd = me.dragHd, - nextHd, - offset = me.tracker.getOffset('point'); - // Only resize if we have dragged any distance in the X dimension... - if (dragHd && offset[0]) { - // resize the dragHd - if (dragHd.flex) { - delete dragHd.flex; - } - Ext.suspendLayouts(); - // Set the new column width. - // Adjusted for the offset from the actual column border that the mousedownb too place at. - me.adjustColumnWidth(offset[0] - me.xDelta); - // In the case of forceFit, change the following Header width. - // Constraining so that neither neighbour can be sized to below minWidth is handled in getConstrainRegion - if (me.headerCt.forceFit) { - nextHd = dragHd.nextNode('gridcolumn:not([hidden]):not([isGroupHeader])'); - if (nextHd && !me.headerInSameGrid(nextHd)) { - nextHd = null; - } - if (nextHd) { - delete nextHd.flex; - nextHd.setWidth(nextHd.getWidth() - offset[0]); - } - } - // Apply the two width changes by laying out the owning HeaderContainer - Ext.resumeLayouts(true); - } - }, - // nextNode can traverse out of this grid, possibly to others on the page, so limit it here - headerInSameGrid: function(header) { - var grid = this.dragHd.up('tablepanel'); - return !!header.up(grid); - }, - disable: function() { - var tracker = this.tracker; - this.disabled = true; - if (tracker) { - tracker.disable(); - } - }, - enable: function() { - var tracker = this.tracker; - this.disabled = false; - if (tracker) { - tracker.enable(); - } - }, - calculateDragX: function(markerOwner) { - return this.tracker.getXY('point')[0] + this.xDelta - markerOwner.getX() - markerOwner.el.getBorderWidth('l'); - }, - getLeftMarkerX: function(markerOwner) { - return this.dragHd.getX() - markerOwner.getX() - markerOwner.el.getBorderWidth('l') - 1; - }, - setMarkerX: function(marker, x) { - marker.setLocalX(x); - }, - adjustConstrainRegion: function(region, t, r, b, l) { - return region.adjust(t, r, b, l); - }, - adjustColumnWidth: function(offsetX) { - this.dragHd.setWidth(this.origWidth + offsetX); - } -}); - -/** - * @private - */ -Ext.define('Ext.grid.header.DragZone', { - extend: 'Ext.dd.DragZone', - colHeaderSelector: '.' + Ext.baseCSSPrefix + 'column-header', - colInnerSelector: '.' + Ext.baseCSSPrefix + 'column-header-inner', - maxProxyWidth: 120, - constructor: function(headerCt) { - var me = this; - me.headerCt = headerCt; - me.ddGroup = me.getDDGroup(); - me.autoGroup = true; - me.callParent([ - headerCt.el - ]); - me.proxy.el.addCls(Ext.baseCSSPrefix + 'grid-col-dd'); - }, - getDDGroup: function() { - return 'header-dd-zone-' + this.headerCt.up('[scrollerOwner]').id; - }, - getDragData: function(e) { - if (e.getTarget(this.colInnerSelector)) { - var header = e.getTarget(this.colHeaderSelector), - headerCmp, ddel; - if (header) { - headerCmp = Ext.getCmp(header.id); - if (!this.headerCt.dragging && headerCmp.draggable && !(headerCmp.isAtStartEdge(e) || headerCmp.isAtEndEdge(e))) { - ddel = document.createElement('div'); - ddel.role = 'presentation'; - ddel.innerHTML = headerCmp.text; - return { - ddel: ddel, - header: headerCmp - }; - } - } - } - return false; - }, - onBeforeDrag: function() { - return !(this.headerCt.dragging || this.disabled); - }, - onInitDrag: function() { - this.headerCt.dragging = true; - this.headerCt.hideMenu(); - this.callParent(arguments); - }, - onDragDrop: function() { - this.headerCt.dragging = false; - this.callParent(arguments); - }, - afterRepair: function() { - this.callParent(); - this.headerCt.dragging = false; - }, - getRepairXY: function() { - return this.dragData.header.el.getXY(); - }, - disable: function() { - this.disabled = true; - }, - enable: function() { - this.disabled = false; - } -}); - -/** - * @private - */ -Ext.define('Ext.grid.header.DropZone', { - extend: 'Ext.dd.DropZone', - colHeaderCls: Ext.baseCSSPrefix + 'column-header', - proxyOffsets: [ - -4, - -9 - ], - constructor: function(headerCt) { - var me = this; - me.headerCt = headerCt; - me.ddGroup = me.getDDGroup(); - me.autoGroup = true; - me.callParent([ - headerCt.el - ]); - }, - destroy: function() { - this.callParent(); - Ext.destroy(this.topIndicator, this.bottomIndicator); - }, - getDDGroup: function() { - return 'header-dd-zone-' + this.headerCt.up('[scrollerOwner]').id; - }, - getTargetFromEvent: function(e) { - return e.getTarget('.' + this.colHeaderCls); - }, - getTopIndicator: function() { - if (!this.topIndicator) { - this.topIndicator = Ext.getBody().createChild({ - role: 'presentation', - cls: Ext.baseCSSPrefix + "col-move-top", - // tell the spec runner to ignore this element when checking if the dom is clean - "data-sticky": true, - html: " " - }); - this.indicatorXOffset = Math.floor((this.topIndicator.dom.offsetWidth + 1) / 2); - } - return this.topIndicator; - }, - getBottomIndicator: function() { - if (!this.bottomIndicator) { - this.bottomIndicator = Ext.getBody().createChild({ - role: 'presentation', - cls: Ext.baseCSSPrefix + "col-move-bottom", - // tell the spec runner to ignore this element when checking if the dom is clean - "data-sticky": true, - html: " " - }); - } - return this.bottomIndicator; - }, - getLocation: function(e, t) { - var x = e.getXY()[0], - region = Ext.fly(t).getRegion(), - pos; - if ((region.right - x) <= (region.right - region.left) / 2) { - pos = "after"; - } else { - pos = "before"; - } - return { - pos: pos, - header: Ext.getCmp(t.id), - node: t - }; - }, - positionIndicator: function(data, node, e) { - var me = this, - dragHeader = data.header, - dropLocation = me.getLocation(e, node), - targetHeader = dropLocation.header, - pos = dropLocation.pos, - nextHd, prevHd, topIndicator, bottomIndicator, topAnchor, bottomAnchor, topXY, bottomXY, headerCtEl, minX, maxX, allDropZones, ln, i, dropZone; - // Avoid expensive CQ lookups and DOM calculations if dropPosition has not changed - if (targetHeader === me.lastTargetHeader && pos === me.lastDropPos) { - return; - } - nextHd = dragHeader.nextSibling('gridcolumn:not([hidden])'); - prevHd = dragHeader.previousSibling('gridcolumn:not([hidden])'); - me.lastTargetHeader = targetHeader; - me.lastDropPos = pos; - // Cannot drag to before non-draggable start column - if (!targetHeader.draggable && pos === 'before' && targetHeader.getIndex() === 0) { - return false; - } - data.dropLocation = dropLocation; - if ((dragHeader !== targetHeader) && ((pos === "before" && nextHd !== targetHeader) || (pos === "after" && prevHd !== targetHeader)) && !targetHeader.isDescendantOf(dragHeader)) { - // As we move in between different DropZones that are in the same - // group (such as the case when in a locked grid), invalidateDrop - // on the other dropZones. - allDropZones = Ext.dd.DragDropManager.getRelated(me); - ln = allDropZones.length; - i = 0; - for (; i < ln; i++) { - dropZone = allDropZones[i]; - if (dropZone !== me && dropZone.invalidateDrop) { - dropZone.invalidateDrop(); - } - } - me.valid = true; - topIndicator = me.getTopIndicator(); - bottomIndicator = me.getBottomIndicator(); - if (pos === 'before') { - topAnchor = 'bc-tl'; - bottomAnchor = 'tc-bl'; - } else { - topAnchor = 'bc-tr'; - bottomAnchor = 'tc-br'; - } - // Calculate arrow positions. Offset them to align exactly with column border line - topXY = topIndicator.getAlignToXY(targetHeader.el, topAnchor); - bottomXY = bottomIndicator.getAlignToXY(targetHeader.el, bottomAnchor); - // constrain the indicators to the viewable section - headerCtEl = me.headerCt.el; - minX = headerCtEl.getX() - me.indicatorXOffset; - maxX = headerCtEl.getX() + headerCtEl.getWidth(); - topXY[0] = Ext.Number.constrain(topXY[0], minX, maxX); - bottomXY[0] = Ext.Number.constrain(bottomXY[0], minX, maxX); - // position and show indicators - topIndicator.setXY(topXY); - bottomIndicator.setXY(bottomXY); - topIndicator.show(); - bottomIndicator.show(); - } else // invalidate drop operation and hide indicators - { - me.invalidateDrop(); - } - }, - invalidateDrop: function() { - this.valid = false; - this.hideIndicators(); - }, - onNodeOver: function(node, dragZone, e, data) { - var me = this, - from = data.header, - doPosition, to, fromPanel, toPanel; - if (data.header.el.dom === node) { - doPosition = false; - } else { - data.isLock = data.isUnlock = data.crossPanel = false; - to = me.getLocation(e, node).header; - // Dragging within the same container - always valid - doPosition = (from.ownerCt === to.ownerCt); - // If from different containers, and they are not sealed, then continue checking - if (!doPosition && (!from.ownerCt.sealed && !to.ownerCt.sealed)) { - doPosition = true; - fromPanel = from.up('tablepanel'); - toPanel = to.up('tablepanel'); - if (fromPanel !== toPanel) { - data.crossPanel = true; - // If it's a lock operation, check that it's allowable. - data.isLock = toPanel.isLocked && !fromPanel.isLocked; - data.isUnlock = !toPanel.isLocked && fromPanel.isLocked; - if ((data.isUnlock && from.lockable === false) || (data.isLock && !from.isLockable())) { - doPosition = false; - } - } - } - } - if (doPosition) { - me.positionIndicator(data, node, e); - } else { - me.valid = false; - } - return me.valid ? me.dropAllowed : me.dropNotAllowed; - }, - hideIndicators: function() { - var me = this; - me.getTopIndicator().hide(); - me.getBottomIndicator().hide(); - me.lastTargetHeader = me.lastDropPos = null; - }, - onNodeOut: function() { - this.hideIndicators(); - }, - /** - * @private - * Used to determine the move position for the view's data columns for nested headers at any level. - */ - getNestedHeader: function(header, first) { - var items = header.items, - pos; - if (header.isGroupHeader && items.length) { - pos = !first ? 'first' : 'last'; - header = this.getNestedHeader(items[pos](), first); - } - return header; - }, - onNodeDrop: function(node, dragZone, e, data) { - // Do not process the upcoming click after this mouseup. It's not a click gesture - this.headerCt.blockNextEvent(); - // Note that dropLocation.pos refers to whether the header is dropped before or after the target node! - if (!this.valid) { - return; - } - var me = this, - dragHeader = data.header, - dropLocation = data.dropLocation, - dropPosition = dropLocation.pos, - targetHeader = dropLocation.header, - fromCt = dragHeader.ownerCt, - fromCtRoot = fromCt.getRootHeaderCt(), - toCt = targetHeader.ownerCt, - // Use the full column manager here, the indices we want are for moving the actual items in the container. - // The HeaderContainer translates this to visible columns for informing the view and firing events. - visibleColumnManager = me.headerCt.visibleColumnManager, - visibleFromIdx = visibleColumnManager.getHeaderIndex(dragHeader), - visibleToIdx, colsToMove, moveMethod, scrollerOwner, savedWidth; - // If we are dragging in between two HeaderContainers that have had the lockable mixin injected we will lock/unlock - // headers in between sections, and then continue with another execution of onNodeDrop to ensure the header is - // dropped into the correct group. - if (data.isLock || data.isUnlock) { - scrollerOwner = fromCt.up('[scrollerOwner]'); - visibleToIdx = toCt.items.indexOf(targetHeader); - if (dropPosition === 'after') { - visibleToIdx++; - } - if (data.isLock) { - scrollerOwner.lock(dragHeader, visibleToIdx, toCt); - } else { - scrollerOwner.unlock(dragHeader, visibleToIdx, toCt); - } - } else // This is a drop within the same HeaderContainer. - { - // For the after position, we need to update the visibleToIdx index. In case it's nested in one or more - // grouped headers, we need to get the last header (or the first, depending on the dropPosition) in the - // items collection for the most deeply-nested header, whether it be first or last in the collection. - // This will yield the header index in the visibleColumnManager, which will correctly maintain a list - // of all the headers. - visibleToIdx = dropPosition === 'after' ? // Get the last header in the most deeply-nested header group and add one. - visibleColumnManager.getHeaderIndex(me.getNestedHeader(targetHeader, 1)) + 1 : // Get the first header in the most deeply-nested header group. - visibleColumnManager.getHeaderIndex(me.getNestedHeader(targetHeader, 0)); - me.invalidateDrop(); - // Cache the width here, we need to get it before we removed it from the DOM - savedWidth = dragHeader.getWidth(); - // Suspend layouts while we sort all this out. - Ext.suspendLayouts(); - // When removing and then adding, the owning gridpanel will be informed of column mutation twice - // Both remove and add handling inform the owning grid. - // The isDDMoveInGrid flag will prevent the remove operation from doing this. - // See Ext.grid.header.Container#onRemove. - fromCt.isDDMoveInGrid = toCt.isDDMoveInGrid = !data.crossPanel; - // ***Move the headers*** - // - // If both drag and target headers are groupHeaders, we have to check and see if they are nested, i.e., - // there are multiple stacked group headers with only subheaders at the lowest level: - // - // +-----------------------------------+ - // | Group 1 | - // |-----------------------------------| - // | Group 2 | - // other |-----------------------------------| other - // headers | Group 3 | headers - // |-----------------------------------| - // | Field3 | Field4 | Field5 | Field6 | - // |===================================| - // | view | - // +-----------------------------------+ - // - // In these cases, we need to mark the groupHeader that is the ownerCt of the targetHeader and then only - // remove the headers up until that (removal of headers is recursive and assumes that any header with no - // children can be safely removed, which is not a safe assumption). - // See Ext.grid.header.Container#onRemove. - if (dragHeader.isGroupHeader && targetHeader.isGroupHeader) { - dragHeader.setNestedParent(targetHeader); - } - // We only need to be concerned with moving the dragHeader component before or after the targetHeader - // component rather than trying to pass indices, which is too ambiguous and could refer to any - // collection at any level of (grouped) header containers. - if (dropPosition === 'before') { - targetHeader.insertNestedHeader(dragHeader); - } else { - // Capitalize the first letter. This will call either ct.moveAfter() or ct.moveBefore(). - moveMethod = 'move' + dropPosition.charAt(0).toUpperCase() + dropPosition.substr(1); - toCt[moveMethod](dragHeader, targetHeader); - } - // ***Move the view data columns*** - // Refresh the view if it's not the last header in a group. If it is the last header, we don't need - // to refresh the view as the headers and the corrresponding data columns will already be correctly - // aligned (think of the group header sitting directly atop the last header in the group). - // Also, it's not necessary to refresh the view if the indices are the same. - // NOTE that targetHeader can be destroyed by this point if it was a group header - // and we just dragged the last column out of it; in that case header's items collection - // will be nulled. - if (visibleToIdx >= 0 && !(targetHeader.isGroupHeader && (!targetHeader.items || !targetHeader.items.length)) && visibleFromIdx !== visibleToIdx) { - colsToMove = dragHeader.isGroupHeader ? dragHeader.query(':not([hidden]):not([isGroupHeader])').length : 1; - // We need to adjust the visibleToIdx when both of the following conditions are met: - // 1. The drag is forward, i.e., the dragHeader is being dragged to the right. - // 2. There is more than one column being dragged, i.e., an entire group. - if ((visibleFromIdx <= visibleToIdx) && colsToMove > 1) { - visibleToIdx -= colsToMove; - } - // It's necessary to lookup the ancestor grid of the grouped header b/c the header could be - // nested at any level. - toCt.getRootHeaderCt().grid.view.moveColumn(visibleFromIdx, visibleToIdx, colsToMove); - } - // We need to always fire a columnmove event. Check for an .ownerCt first in case this is a - // grouped header. - fromCtRoot.fireEvent('columnmove', fromCt, dragHeader, visibleFromIdx, visibleToIdx); - fromCt.isDDMoveInGrid = toCt.isDDMoveInGrid = false; - // Group headers skrinkwrap their child headers. - // Therefore a child header may not flex; it must contribute a fixed width. - // But we restore the flex value when moving back into the main header container - // - // Note that we don't need to save the flex if coming from another group header b/c it couldn't - // have had one! - if (toCt.isGroupHeader && !fromCt.isGroupHeader) { - // Adjust the width of the "to" group header only if we dragged in from somewhere else. - // If not within the same container. - if (fromCt !== toCt) { - dragHeader.savedFlex = dragHeader.flex; - delete dragHeader.flex; - dragHeader.width = savedWidth; - } - } else if (!fromCt.isGroupHeader) { - if (dragHeader.savedFlex) { - dragHeader.flex = dragHeader.savedFlex; - delete dragHeader.width; - } - } - Ext.resumeLayouts(true); - } - } -}); -// Ext.grid.header.Container will handle the removal of empty groups, don't handle it here. - -/** - * @private - */ -Ext.define('Ext.grid.plugin.HeaderReorderer', { - extend: 'Ext.plugin.Abstract', - requires: [ - 'Ext.grid.header.DragZone', - 'Ext.grid.header.DropZone' - ], - alias: 'plugin.gridheaderreorderer', - init: function(headerCt) { - this.headerCt = headerCt; - headerCt.on({ - boxready: this.onHeaderCtRender, - single: true, - scope: this - }); - }, - destroy: function() { - var me = this; - // The grid may happen to never render - me.headerCt.un('boxready', me.onHeaderCtRender, me); - Ext.destroy(me.dragZone, me.dropZone); - me.headerCt = me.dragZone = me.dropZone = null; - me.callParent(); - }, - onHeaderCtRender: function() { - var me = this; - me.dragZone = new Ext.grid.header.DragZone(me.headerCt); - me.dropZone = new Ext.grid.header.DropZone(me.headerCt); - if (me.disabled) { - me.dragZone.disable(); - } - }, - enable: function() { - this.disabled = false; - if (this.dragZone) { - this.dragZone.enable(); - } - }, - disable: function() { - this.disabled = true; - if (this.dragZone) { - this.dragZone.disable(); - } - } -}); - -/** - * Headercontainer is a docked container (_`top` or `bottom` only_) that holds the - * headers ({@link Ext.grid.column.Column grid columns}) of a - * {@link Ext.grid.Panel grid} or {@link Ext.tree.Panel tree}. The headercontainer - * handles resizing, moving, and hiding columns. As columns are hidden, moved or - * resized, the headercontainer triggers changes within the grid or tree's - * {@link Ext.view.Table view}. You will not generally need to instantiate this class - * directly. - * - * You may use the - * {@link Ext.panel.Table#method-getHeaderContainer getHeaderContainer()} - * accessor method to access the tree or grid's headercontainer. - * - * Grids and trees also have an alias to the two more useful headercontainer methods: - * - * - **{@link Ext.panel.Table#method-getColumns getColumns}** - aliases - * {@link Ext.grid.header.Container#getGridColumns} - * - **{@link Ext.panel.Table#method-getVisibleColumns getVisibleColumns}** - aliases - * {@link Ext.grid.header.Container#getVisibleGridColumns} - */ -Ext.define('Ext.grid.header.Container', { - extend: 'Ext.container.Container', - requires: [ - 'Ext.grid.ColumnLayout', - 'Ext.grid.plugin.HeaderResizer', - 'Ext.grid.plugin.HeaderReorderer', - 'Ext.util.KeyNav' - ], - uses: [ - 'Ext.grid.column.Column', - 'Ext.grid.ColumnManager', - 'Ext.menu.Menu', - 'Ext.menu.CheckItem', - 'Ext.menu.Separator' - ], - mixins: [ - 'Ext.util.FocusableContainer' - ], - border: true, - alias: 'widget.headercontainer', - baseCls: Ext.baseCSSPrefix + 'grid-header-ct', - dock: 'top', - /** - * @cfg {Number} weight - * HeaderContainer overrides the default weight of 0 for all docked items to 100. - * This is so that it has more priority over things like toolbars. - */ - weight: 100, - defaultType: 'gridcolumn', - detachOnRemove: false, - /** - * @cfg {Number} defaultWidth - * Width of the header if no width or flex is specified. - */ - defaultWidth: 100, - /** - * @cfg {Boolean} [sealed=false] - * Specify as `true` to constrain column dragging so that a column cannot be dragged into or out of this column. - * - * **Note that this config is only valid for column headers which contain child column headers, eg:** - * { - * sealed: true - * text: 'ExtJS', - * columns: [{ - * text: '3.0.4', - * dataIndex: 'ext304' - * }, { - * text: '4.1.0', - * dataIndex: 'ext410' - * } - * } - * - */ - // - sortAscText: 'Sort Ascending', - // - // - sortDescText: 'Sort Descending', - // - // - sortClearText: 'Clear Sort', - // - // - columnsText: 'Columns', - // - headerOpenCls: Ext.baseCSSPrefix + 'column-header-open', - menuSortAscCls: Ext.baseCSSPrefix + 'hmenu-sort-asc', - menuSortDescCls: Ext.baseCSSPrefix + 'hmenu-sort-desc', - menuColsIcon: Ext.baseCSSPrefix + 'cols-icon', - blockEvents: false, - dragging: false, - // May be set to false by a SptreadSheetSelectionModel - sortOnClick: true, - // Disable FocusableContainer behavior by default, since we only want it - // to be enabled for the root header container (we'll set the flag in initComponent) - enableFocusableContainer: false, - childHideCount: 0, - /** - * @property {Boolean} isGroupHeader - * True if this HeaderContainer is in fact a group header which contains sub headers. - */ - /** - * @cfg {Boolean} sortable - * Provides the default sortable state for all Headers within this HeaderContainer. - * Also turns on or off the menus in the HeaderContainer. Note that the menu is - * shared across every header and therefore turning it off will remove the menu - * items for every header. - */ - sortable: true, - /** - * @cfg {Boolean} [enableColumnHide=true] - * False to disable column hiding within this grid. - */ - enableColumnHide: true, - /** - * @event columnresize - * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers. - * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition - * @param {Number} width - */ - /** - * @event headerclick - * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers. - * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition - * @param {Ext.event.Event} e - * @param {HTMLElement} t - */ - /** - * @event headercontextmenu - * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers. - * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition - * @param {Ext.event.Event} e - * @param {HTMLElement} t - */ - /** - * @event headertriggerclick - * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers. - * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition - * @param {Ext.event.Event} e - * @param {HTMLElement} t - */ - /** - * @event columnmove - * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers. - * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition - * @param {Number} fromIdx - * @param {Number} toIdx - */ - /** - * @event columnhide - * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers. - * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition - */ - /** - * @event columnshow - * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers. - * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition - */ - /** - * @event columnschanged - * Fired after the columns change in any way, when a column has been hidden or shown, or when a column - * is added to or removed from this header container. - * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers. - */ - /** - * @event sortchange - * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers. - * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition - * @param {String} direction - */ - /** - * @event menucreate - * Fired immediately after the column header menu is created. - * @param {Ext.grid.header.Container} ct This instance - * @param {Ext.menu.Menu} menu The Menu that was created - */ - /** - * @event headermenucreate - * Fired immediately after the column header menu is created. - * @param {Ext.panel.Table} grid This grid instance - * @param {Ext.menu.Menu} menu The Menu that was created - * @param {Ext.grid.header.Container} headerCt This header container - * @member Ext.panel.Table - */ - initComponent: function() { - var me = this; - me.plugins = me.plugins || []; - me.defaults = me.defaults || {}; - // TODO: Pass in configurations to turn on/off dynamic - // resizing and disable resizing all together - // Only set up a Resizer and Reorderer for the topmost HeaderContainer. - // Nested Group Headers are themselves HeaderContainers - if (!me.isColumn) { - if (me.enableColumnResize) { - me.resizer = new Ext.grid.plugin.HeaderResizer(); - me.plugins.push(me.resizer); - } - if (me.enableColumnMove) { - me.reorderer = new Ext.grid.plugin.HeaderReorderer(); - me.plugins.push(me.reorderer); - } - } - // If this is a leaf column header, and is NOT functioning as a container, - // use Container layout with a no-op calculate method. - if (me.isColumn && !me.isGroupHeader) { - if (!me.items || me.items.length === 0) { - me.isContainer = me.isFocusableContainer = false; - me.focusable = true; - me.layout = { - type: 'container', - calculate: Ext.emptyFn - }; - } - } else // HeaderContainer and Group header needs a gridcolumn layout. - { - me.layout = Ext.apply({ - type: 'gridcolumn', - align: 'stretch' - }, me.initialConfig.layout); - // All HeaderContainers need to know this so that leaf Columns can adjust for cell border width if using content box model - me.defaults.columnLines = me.columnLines; - // If the header isn't a column ([isColumn] or [isGroupHeader]), then it's the root header. - if (!me.isGroupHeader) { - me.isRootHeader = true; - // The root header is a focusableContainer if it's not carrying hidden headers. - if (!me.hiddenHeaders) { - me.enableFocusableContainer = true; - me.ariaRole = 'rowgroup'; - } - // Create column managers for the root header. - me.columnManager = new Ext.grid.ColumnManager(false, me); - me.visibleColumnManager = new Ext.grid.ColumnManager(true, me); - // In the grid config, if grid.columns is a header container instance and not a columns - // config, then it currently has no knowledge of a containing grid. Create the column - // manager now and bind it to the grid later in Ext.panel.Table:initComponent(). - // - // In most cases, though, grid.columns will be a config, so the grid is already known - // and the column manager can be bound to it. - if (me.grid) { - me.grid.columnManager = me.columnManager; - me.grid.visibleColumnManager = me.visibleColumnManager; - } - } else { - // Is a group header, also create column managers. - me.visibleColumnManager = new Ext.grid.ColumnManager(true, me); - me.columnManager = new Ext.grid.ColumnManager(false, me); - } - } - me.menuTask = new Ext.util.DelayedTask(me.updateMenuDisabledState, me); - me.callParent(); - }, - insertNestedHeader: function(moveHeader) { - var me = this, - fromCt = moveHeader.ownerCt, - toCt = me.ownerCt, - layoutOwner = toCt.layout.owner, - toIndex; - if (fromCt) { - if (me.isGroupHeader && !toCt.isNestedParent) { - toIndex = layoutOwner.items.indexOf(me); - } - fromCt.remove(moveHeader, false); - } - if (toIndex === undefined) { - toIndex = layoutOwner.items.indexOf(me); - } - layoutOwner.insert(toIndex, moveHeader); - }, - isNested: function() { - return !!this.getRootHeaderCt().down('[isNestedParent]'); - }, - isNestedGroupHeader: function() { - // The owner only has one item that isn't hidden and it's me; hide the owner. - var header = this, - items = header.getRefOwner().query('>:not([hidden])'); - return (items.length === 1 && items[0] === header); - }, - maybeShowNestedGroupHeader: function() { - // Group headers are special in that they are auto-hidden when their subheaders are all - // hidden and auto-shown when the first subheader is reshown. They are the only headers - // that should now be auto-shown or -hidden. - // - // It follows that since group headers are dictated by some automation depending upon the - // state of their child items that all group headers should be shown if anyone in the - // hierarchy is shown since these special group headers only contain one child, which is - // the next group header in the stack. - // This only should apply to the following grouped header scenario: - // - // +-----------------------------------+ - // | Group 1 | - // |-----------------------------------| - // | Group 2 | - // other |-----------------------------------| other - // headers | Group 3 | headers - // |-----------------------------------| - // | Field3 | Field4 | Field5 | Field6 | - // |===================================| - // | view | - // +-----------------------------------+ - // - var items = this.items, - item; - if (items && items.length === 1 && (item = items.getAt(0)) && item.hidden) { - item.show(); - } - }, - setNestedParent: function(target) { - // Here we need to prevent the removal of ancestor group headers from occuring if a flag is set. This - // is needed when there are stacked group headers and only the deepest nested group header has leaf items - // in its collection. In this specific scenario, the group headers above it only have 1 item, which is its - // child nested group header. - // - // If we don't set this flag, then all of the grouped headers will be recursively removed all the way up to - // the root container b/c Ext.grid.header.Container#onRemove will remove all containers that don't contain - // any items. - // - // Note that if an ownerCt only has one item, then we know that this item is the group header that we're - // currently dragging. - // - // Also, note that we mark the owner as the target header because everything up to that should be removed. - // - // We have to reset any previous headers that may have been target.ownerCts! - target.isNestedParent = false; - target.ownerCt.isNestedParent = !!(this.ownerCt.items.length === 1 && target.ownerCt.items.length === 1); - }, - initEvents: function() { - var me = this, - onHeaderCtEvent, listeners; - me.callParent(); - // If this is top level, listen for events to delegate to descendant headers. - if (!me.isColumn && !me.isGroupHeader) { - onHeaderCtEvent = me.onHeaderCtEvent; - listeners = { - click: onHeaderCtEvent, - dblclick: onHeaderCtEvent, - contextmenu: onHeaderCtEvent, - mouseover: me.onHeaderCtMouseOver, - mouseout: me.onHeaderCtMouseOut, - scope: me - }; - if (Ext.supports.Touch) { - listeners.longpress = me.onHeaderCtLongPress; - } - me.mon(me.el, listeners); - } - }, - onHeaderCtEvent: function(e, t) { - var me = this, - headerEl = me.getHeaderElByEvent(e), - header, targetEl, activeHeader; - if (me.longPressFired) { - // if we just showed the menu as a result of a longpress, do not process - // the click event and sort the column. - me.longPressFired = false; - return; - } - if (headerEl && !me.blockEvents) { - header = Ext.getCmp(headerEl.id); - if (header) { - targetEl = header[header.clickTargetName]; - // If there's no possibility that the mouseEvent was on child header items, - // or it was definitely in our titleEl, then process it - if ((!header.isGroupHeader && !header.isContainer) || e.within(targetEl)) { - if (e.type === 'click' || e.type === 'tap') { - // The header decides which header to activate on click - // on Touch, anywhere in the splitter zone activates - // the left header. - activeHeader = header.onTitleElClick(e, targetEl, me.sortOnClick); - if (activeHeader) { - // If activated by touch, there is no trigger el to align with, so align to the header element. - me.onHeaderTriggerClick(activeHeader, e, e.pointerType === 'touch' ? activeHeader.el : activeHeader.triggerEl); - } else { - me.onHeaderClick(header, e, t); - } - } else if (e.type === 'contextmenu') { - me.onHeaderContextMenu(header, e, t); - } else if (e.type === 'dblclick' && header.resizable) { - header.onTitleElDblClick(e, targetEl.dom); - } - } - } - } - }, - blockNextEvent: function() { - this.blockEvents = true; - Ext.asap(this.unblockEvents, this); - }, - unblockEvents: function() { - this.blockEvents = false; - }, - onHeaderCtMouseOver: function(e, t) { - var headerEl, header, targetEl; - // Only proces the mouse entering this HeaderContainer. - // From header to header, and exiting this HeaderContainer we track using mouseout events. - if (!e.within(this.el, true)) { - headerEl = e.getTarget('.' + Ext.grid.column.Column.prototype.baseCls); - header = headerEl && Ext.getCmp(headerEl.id); - if (header) { - targetEl = header[header.clickTargetName]; - if (e.within(targetEl)) { - header.onTitleMouseOver(e, targetEl.dom); - } - } - } - }, - onHeaderCtMouseOut: function(e, t) { - var headerSelector = '.' + Ext.grid.column.Column.prototype.baseCls, - outHeaderEl = e.getTarget(headerSelector), - inHeaderEl = e.getRelatedTarget(headerSelector), - header, targetEl; - // It's a mouseenter/leave, not an internal element change within a Header - if (outHeaderEl !== inHeaderEl) { - if (outHeaderEl) { - header = Ext.getCmp(outHeaderEl.id); - if (header) { - targetEl = header[header.clickTargetName]; - header.onTitleMouseOut(e, targetEl.dom); - } - } - if (inHeaderEl) { - header = Ext.getCmp(inHeaderEl.id); - if (header) { - targetEl = header[header.clickTargetName]; - header.onTitleMouseOver(e, targetEl.dom); - } - } - } - }, - onHeaderCtLongPress: function(e) { - var me = this, - headerEl = me.getHeaderElByEvent(e), - header = Ext.getCmp(headerEl.id); - if (!header.menuDisabled) { - me.longPressFired = true; - me.showMenuBy(e, headerEl, header); - } - }, - getHeaderElByEvent: function(e) { - return e.getTarget('.' + Ext.grid.column.Column.prototype.baseCls); - }, - isLayoutRoot: function() { - // Since we're docked, the width is always calculated - // If we're hidden, the height is explicitly 0, which - // means we'll be considered a layout root. However, we - // still need the view to layout to update the underlying - // table to match the size. - if (this.hiddenHeaders) { - return false; - } - return this.callParent(); - }, - // Find the topmost HeaderContainer - getRootHeaderCt: function() { - var me = this; - return me.isRootHeader ? me : me.up('[isRootHeader]'); - }, - onDestroy: function() { - var me = this; - if (me.menu) { - me.menu.un('hide', me.onMenuHide, me); - } - me.menuTask.cancel(); - me.callParent(); - Ext.destroy(me.visibleColumnManager, me.columnManager, me.menu); - me.columnManager = me.visibleColumnManager = null; - }, - applyColumnsState: function(columns, storeState) { - if (!columns || !columns.length) { - return; - } - var me = this, - items = me.items.items, - count = items.length, - i = 0, - length = columns.length, - c, col, columnState, index, - moved = false, - newOrder = [], - stateHash = {}, - newCols = []; - // Create state lookup hash - // { - // col_name: { - // index: 0, - // width: 100 - // }, - // col_email: { - // index: 1, - // width: 100 - // } - // } - for (c = 0; c < length; c++) { - columnState = columns[c]; - columnState.index = c; - stateHash[columnState.id] = columnState; - } - for (i = 0; i < count; i++) { - col = items[i]; - columnState = stateHash[col.getStateId()]; - // There's a column state for this column. - // Add it to the newOrder array at the specified index - if (columnState) { - index = columnState.index; - newOrder[index] = col; - if (i !== index) { - moved = true; - } - if (col.applyColumnState) { - col.applyColumnState(columnState, storeState); - } - } else // A new column. - // It must be inserted at this index after state restoration, - { - newCols.push({ - index: i, - column: col - }); - } - } - // If any saved columns were missing, close the gaps where they were - newOrder = Ext.Array.clean(newOrder); - // New column encountered. - // Insert them into the newOrder at their configured position - length = newCols.length; - if (length) { - for (i = 0; i < length; i++) { - columnState = newCols[i]; - index = columnState.index; - if (index < newOrder.length) { - moved = true; - Ext.Array.splice(newOrder, index, 0, columnState.column); - } else { - newOrder.push(columnState.column); - } - } - } - if (moved) { - // This flag will prevent the groupheader from being removed by its owner when it (temporarily) has no child items. - me.applyingState = true; - me.removeAll(false); - delete me.applyingState; - me.add(newOrder); - me.purgeCache(); - } - }, - getColumnsState: function() { - var me = this, - columns = [], - state; - me.items.each(function(col) { - state = col.getColumnState && col.getColumnState(); - if (state) { - columns.push(state); - } - }); - return columns; - }, - // Invalidate column cache on add - // We cannot refresh the View on every add because this method is called - // when the HeaderDropZone moves Headers around, that will also refresh the view - onAdd: function(c) { - var me = this; - if (!me._usedIDs) { - me._usedIDs = {}; - } - if (me._usedIDs[c.headerId]) { - Ext.log.warn(this.$className + ' attempted to reuse an existing id: ' + c.headerId); - } - me._usedIDs[c.headerId] = true; - me.callParent(arguments); - me.onHeadersChanged(c, me.isDDMoveInGrid); - }, - move: function(fromIdx, toIdx) { - var me = this, - items = me.items, - headerToMove; - if (fromIdx.isComponent) { - headerToMove = fromIdx; - fromIdx = items.indexOf(headerToMove); - } else { - headerToMove = items.getAt(fromIdx); - } - // Take real grid column index of column being moved - headerToMove.visibleFromIdx = me.getRootHeaderCt().visibleColumnManager.indexOf(headerToMove); - me.callParent(arguments); - }, - onMove: function(headerToMove, fromIdx, toIdx) { - var me = this, - gridHeaderCt = me.getRootHeaderCt(), - gridVisibleColumnManager = gridHeaderCt.visibleColumnManager, - numColsToMove = 1, - visibleToIdx; - // Purges cache so that indexOf returns new position of header - me.onHeadersChanged(headerToMove, true); - visibleToIdx = gridVisibleColumnManager.indexOf(headerToMove); - if (visibleToIdx >= headerToMove.visibleFromIdx) { - visibleToIdx++; - } - me.callParent(arguments); - // If what is being moved is a group header, then pass the correct column count - if (headerToMove.isGroupHeader) { - numColsToMove = headerToMove.visibleColumnManager.getColumns().length; - } - gridHeaderCt.onHeaderMoved(headerToMove, numColsToMove, headerToMove.visibleFromIdx, visibleToIdx); - }, - // @private - maybeContinueRemove: function() { - var me = this; - // Note that if the column is a group header and is the current target of a drag, we don't want to remove it - // if it since it could be one of any number of (empty) nested group headers. - // See #isNested. - // - // There are also other scenarios in which the remove should not occur. For instance, when applying column - // state to a groupheader, the subheaders are all removed before being re-added in their stateful order, - // and the groupheader should not be removed in the meantime. - // See EXTJS-17577. - return (me.isGroupHeader && !me.applyingState) && !me.isNestedParent && me.ownerCt && !me.items.getCount(); - }, - // Invalidate column cache on remove - // We cannot refresh the View on every remove because this method is called - // when the HeaderDropZone moves Headers around, that will also refresh the view - onRemove: function(c, isDestroying) { - var me = this, - ownerCt = me.ownerCt, - lastHiddenHeader = c.lastHiddenHeader; - me.callParent([ - c, - isDestroying - ]); - if (!me._usedIDs) { - me._usedIDs = {}; - } - delete me._usedIDs[c.headerId]; - if (!me.destroying) { - // isDDMoveInGrid flag set by Ext.grid.header.DropZone when moving into another container *within the same grid*. - // This stops header change processing from being executed twice, once on remove and then on the subsequent add. - if (!me.isDDMoveInGrid) { - me.onHeadersChanged(c, false); - } - if (me.maybeContinueRemove()) { - // Detach the header from the DOM here. Since we're removing and destroying the container, - // the inner DOM may get overwritten, since Container::deatchOnRemove gets processed after - // onRemove. - if (c.rendered) { - me.detachComponent(c); - } - // If we don't have any items left and we're a group, remove ourselves. - // This will cascade up if necessary - Ext.suspendLayouts(); - ownerCt.remove(me); - Ext.resumeLayouts(true); - } - } - }, - // Private - // Called to clear all caches of columns whenever columns are added, removed to just moved. - // We need to be informed if it's just a move operation so that we don't call the heavier - // grid.onHeadersChanged which refreshes the view. - // The onMove handler ensures that grid.inHeaderMove is called which just swaps cells. - onHeadersChanged: function(c, isMove) { - var gridPanel, - gridHeaderCt = this.getRootHeaderCt(); - // Each HeaderContainer up the chain must have its cache purged so that its getGridColumns method will return correct results. - this.purgeHeaderCtCache(this); - if (gridHeaderCt) { - gridHeaderCt.onColumnsChanged(); - if (!c.isGroupHeader) { - gridPanel = gridHeaderCt.ownerCt; - // If it an add or remove operation causing this header change call, then inform the grid which refreshes. - // Moving calls the onHeaderMoved method of the grid which just swaps cells. - if (gridPanel && !isMove) { - gridPanel.onHeadersChanged(gridHeaderCt, c); - } - } - } - }, - // Private - onHeaderMoved: function(header, colsToMove, fromIdx, toIdx) { - var me = this, - gridSection = me.ownerCt; - if (me.rendered) { - if (gridSection && gridSection.onHeaderMove) { - gridSection.onHeaderMove(me, header, colsToMove, fromIdx, toIdx); - } - me.fireEvent('columnmove', me, header, fromIdx, toIdx); - } - }, - // Private - // Only called on the grid's headerCt. - // Called whenever a column is added or removed or moved at any level below. - // Ensures that the gridColumns caches are cleared. - onColumnsChanged: function() { - var me = this, - menu = me.menu, - columnItemSeparator, columnItem; - if (me.rendered) { - me.fireEvent('columnschanged', me); - // Column item (and its associated menu) menu has to be destroyed (if it exits) when columns are changed. - // It will be recreated just before the main container menu is next shown. - if (menu && (columnItemSeparator = menu.child('#columnItemSeparator'))) { - columnItem = menu.child('#columnItem'); - // Destroy the column visibility items - // They will be recreated before the next show - columnItemSeparator.destroy(); - columnItem.destroy(); - } - } - }, - /** - * @private - */ - lookupComponent: function(comp) { - var result = this.callParent(arguments); - // Apply default width unless it's a group header (in which case it must be left to shrinkwrap), or it's flexed. - // Test whether width is undefined so that width: null can be used to have the header shrinkwrap its text. - if (!result.isGroupHeader && result.width === undefined && !result.flex) { - result.width = this.defaultWidth; - } - return result; - }, - /** - * @private - * Synchronize column UI visible sort state with Store's sorters. - */ - setSortState: function() { - var store = this.up('[store]').store, - columns = this.visibleColumnManager.getColumns(), - len = columns.length, - i, header, sorter; - for (i = 0; i < len; i++) { - header = columns[i]; - // Access the column's custom sorter in preference to one keyed on the data index. - sorter = header.getSorter(); - if (sorter) { - // If the column was configured with a sorter, we must check that the sorter - // is part of the store's sorter collection to update the UI to the correct state. - // The store may not actually BE sorted by that sorter. - if (!store.getSorters().contains(sorter)) { - sorter = null; - } - } else { - sorter = store.getSorters().get(header.getSortParam()); - } - // Important: A null sorter for this column will *clear* the UI sort indicator. - header.setSortState(sorter); - } - }, - getHeaderMenu: function() { - var menu = this.getMenu(), - item; - if (menu) { - item = menu.child('#columnItem'); - if (item) { - return item.menu; - } - } - return null; - }, - onHeaderVisibilityChange: function(header, visible) { - var me = this, - menu = me.getHeaderMenu(), - item; - // Invalidate column collections upon column hide/show - me.purgeHeaderCtCache(header.ownerCt); - if (menu) { - // If the header was hidden programmatically, sync the Menu state - item = me.getMenuItemForHeader(menu, header); - if (item) { - item.setChecked(visible, true); - } - // delay this since the headers may fire a number of times if we're hiding/showing groups - if (menu.isVisible()) { - me.menuTask.delay(50); - } - } - }, - updateMenuDisabledState: function(menu) { - var me = this, - columns = me.query('gridcolumn:not([hidden])'), - i, - len = columns.length, - item, checkItem, method; - // If called from menu creation, it will be passed to avoid infinite recursion - if (!menu) { - menu = me.getMenu(); - } - for (i = 0; i < len; ++i) { - item = columns[i]; - checkItem = me.getMenuItemForHeader(menu, item); - if (checkItem) { - method = item.isHideable() ? 'enable' : 'disable'; - if (checkItem.menu) { - method += 'CheckChange'; - } - checkItem[method](); - } - } - }, - getMenuItemForHeader: function(menu, header) { - return header ? menu.down('menucheckitem[headerId=' + header.id + ']') : null; - }, - onHeaderShow: function(header) { - var me = this, - ownerCt = me.ownerCt, - lastHiddenHeader = header.lastHiddenHeader; - if (!ownerCt) { - return; - } - if (me.forceFit) { - delete me.flex; - } - // If lastHiddenHeader exists we know that header is a groupHeader and if all its subheaders - // are hidden then we need to show the last one that was hidden. - if (lastHiddenHeader && !header.query('[hidden=false]').length) { - lastHiddenHeader.show(); - header.lastHiddenHeader = null; - } - me.onHeaderVisibilityChange(header, true); - ownerCt.onHeaderShow(me, header); - me.fireEvent('columnshow', me, header); - me.fireEvent('columnschanged', this); - }, - onHeaderHide: function(header) { - var me = this, - ownerCt = me.ownerCt; - if (!ownerCt) { - return; - } - me.onHeaderVisibilityChange(header, false); - ownerCt.onHeaderHide(me, header); - me.fireEvent('columnhide', me, header); - me.fireEvent('columnschanged', this); - }, - onHeaderResize: function(header, w) { - var me = this, - gridSection = me.ownerCt; - if (gridSection) { - gridSection.onHeaderResize(me, header, w); - } - me.fireEvent('columnresize', me, header, w); - }, - onHeaderClick: function(header, e, t) { - var me = this, - selModel = header.getView().getSelectionModel(); - header.fireEvent('headerclick', me, header, e, t); - if (me.fireEvent('headerclick', me, header, e, t) !== false) { - if (selModel.onHeaderClick) { - selModel.onHeaderClick(me, header, e); - } - } - }, - onHeaderContextMenu: function(header, e, t) { - header.fireEvent('headercontextmenu', this, header, e, t); - this.fireEvent('headercontextmenu', this, header, e, t); - }, - onHeaderTriggerClick: function(header, e, t) { - var me = this; - if (header.fireEvent('headertriggerclick', me, header, e, t) !== false && me.fireEvent('headertriggerclick', me, header, e, t) !== false) { - // If menu is already active... - if (header.activeMenu) { - // Click/tap toggles the menu visibility. - if (e.pointerType) { - header.activeMenu.hide(); - } else { - header.activeMenu.focus(); - } - } else { - me.showMenuBy(e, t, header); - } - } - }, - /** - * @private - * - * Shows the column menu under the target element passed. This method is used when the trigger element on the column - * header is clicked on and rarely should be used otherwise. - * - * @param {Ext.event.Event} [event] The event which triggered the current handler. If omitted - * or a key event, the menu autofocuses its first item. - * @param {HTMLElement/Ext.dom.Element} t The target to show the menu by - * @param {Ext.grid.header.Container} header The header container that the trigger was clicked on. - */ - showMenuBy: function(clickEvent, t, header) { - var menu = this.getMenu(), - ascItem = menu.down('#ascItem'), - descItem = menu.down('#descItem'), - sortableMth, - isTouch = clickEvent && clickEvent.pointerType === 'touch'; - // Use ownerCmp as the upward link. Menus *must have no ownerCt* - they are global floaters. - // Upward navigation is done using the up() method. - menu.activeHeader = menu.ownerCmp = header; - header.setMenuActive(menu); - // enable or disable asc & desc menu items based on header being sortable - sortableMth = header.sortable ? 'enable' : 'disable'; - if (ascItem) { - ascItem[sortableMth](); - } - if (descItem) { - descItem[sortableMth](); - } - // Pointer-invoked menus do not auto focus, key invoked ones do. - menu.autoFocus = !clickEvent || clickEvent.keyCode; - // For longpress t is the header, for click/hover t is the trigger - menu.showBy(t, 'tl-bl?'); - // Menu show was vetoed by event handler - clear context - if (!menu.isVisible()) { - this.onMenuHide(menu); - } - }, - hideMenu: function() { - if (this.menu) { - this.menu.hide(); - } - }, - // remove the trigger open class when the menu is hidden - onMenuHide: function(menu) { - menu.activeHeader.setMenuActive(false); - }, - purgeHeaderCtCache: function(headerCt) { - while (headerCt) { - headerCt.purgeCache(); - if (headerCt.isRootHeader) { - return; - } - headerCt = headerCt.ownerCt; - } - }, - purgeCache: function() { - var me = this, - visibleColumnManager = me.visibleColumnManager, - columnManager = me.columnManager; - // Delete column cache - column order has changed. - me.gridVisibleColumns = me.gridDataColumns = me.hideableColumns = null; - // ColumnManager. Only the top - if (visibleColumnManager) { - visibleColumnManager.invalidate(); - columnManager.invalidate(); - } - }, - /** - * Gets the menu (and will create it if it doesn't already exist) - * @private - */ - getMenu: function() { - var me = this, - grid = me.view && me.view.ownerGrid; - if (!me.menu) { - me.menu = new Ext.menu.Menu({ - hideOnParentHide: false, - // Persists when owning ColumnHeader is hidden - items: me.getMenuItems(), - listeners: { - beforeshow: me.beforeMenuShow, - hide: me.onMenuHide, - scope: me - } - }); - me.fireEvent('menucreate', me, me.menu); - if (grid) { - grid.fireEvent('headermenucreate', grid, me.menu, me); - } - } - return me.menu; - }, - // Render our menus to the first enclosing scrolling element so that they scroll with the grid - beforeMenuShow: function(menu) { - var me = this, - columnItem = menu.child('#columnItem'), - hideableColumns, insertPoint; - // If a change of column structure caused destruction of the column menu item - // or the main menu was created without the column menu item because it began with no hideable headers - // Then create it and its menu now. - if (!columnItem) { - hideableColumns = me.enableColumnHide ? me.getColumnMenu(me) : null; - // Insert after the "Sort Ascending", "Sort Descending" menu items if they are present. - insertPoint = me.sortable ? 2 : 0; - if (hideableColumns && hideableColumns.length) { - menu.insert(insertPoint, [ - { - itemId: 'columnItemSeparator', - xtype: 'menuseparator' - }, - { - itemId: 'columnItem', - text: me.columnsText, - iconCls: me.menuColsIcon, - menu: { - items: hideableColumns - }, - hideOnClick: false - } - ]); - } - } - me.updateMenuDisabledState(me.menu); - }, - // TODO: rendering the menu to the nearest overlfowing ancestor has been disabled - // since DomQuery is no longer available by default in 5.0 - // if (!menu.rendered) { - // menu.render(this.el.up('{overflow=auto}') || document.body); - // } - /** - * Returns an array of menu items to be placed into the shared menu - * across all headers in this header container. - * @return {Array} menuItems - */ - getMenuItems: function() { - var me = this, - menuItems = [], - hideableColumns = me.enableColumnHide ? me.getColumnMenu(me) : null; - if (me.sortable) { - menuItems = [ - { - itemId: 'ascItem', - text: me.sortAscText, - iconCls: me.menuSortAscCls, - handler: me.onSortAscClick, - scope: me - }, - { - itemId: 'descItem', - text: me.sortDescText, - iconCls: me.menuSortDescCls, - handler: me.onSortDescClick, - scope: me - } - ]; - } - if (hideableColumns && hideableColumns.length) { - if (me.sortable) { - menuItems.push({ - itemId: 'columnItemSeparator', - xtype: 'menuseparator' - }); - } - menuItems.push({ - itemId: 'columnItem', - text: me.columnsText, - iconCls: me.menuColsIcon, - menu: hideableColumns, - hideOnClick: false - }); - } - return menuItems; - }, - // sort asc when clicking on item in menu - onSortAscClick: function() { - var menu = this.getMenu(), - activeHeader = menu.activeHeader; - activeHeader.sort('ASC'); - }, - // sort desc when clicking on item in menu - onSortDescClick: function() { - var menu = this.getMenu(), - activeHeader = menu.activeHeader; - activeHeader.sort('DESC'); - }, - /** - * Returns an array of menu CheckItems corresponding to all immediate children - * of the passed Container which have been configured as hideable. - */ - getColumnMenu: function(headerContainer) { - var menuItems = [], - i = 0, - item, - items = headerContainer.query('>gridcolumn[hideable]'), - itemsLn = items.length, - menuItem; - for (; i < itemsLn; i++) { - item = items[i]; - menuItem = new Ext.menu.CheckItem({ - text: item.menuText || item.text, - checked: !item.hidden, - hideOnClick: false, - headerId: item.id, - menu: item.isGroupHeader ? this.getColumnMenu(item) : undefined, - checkHandler: this.onColumnCheckChange, - scope: this - }); - menuItems.push(menuItem); - } - // Prevent creating a submenu if we have no items - return menuItems.length ? menuItems : null; - }, - onColumnCheckChange: function(checkItem, checked) { - var header = Ext.getCmp(checkItem.headerId); - if (header.rendered) { - header[checked ? 'show' : 'hide'](); - } else { - header.hidden = !checked; - } - }, - /** - * Returns the number of grid columns descended from this HeaderContainer. - * Group Columns are HeaderContainers. All grid columns are returned, including hidden ones. - */ - getColumnCount: function() { - return this.getGridColumns().length; - }, - /** - * Gets the full width of all columns that are visible for setting width of tables. - */ - getTableWidth: function() { - var fullWidth = 0, - headers = this.getVisibleGridColumns(), - headersLn = headers.length, - i; - for (i = 0; i < headersLn; i++) { - fullWidth += headers[i].getCellWidth() || 0; - } - return fullWidth; - }, - /** - * Returns an array of the **visible** columns in the grid. This goes down to the - * lowest column header level, and does not return **grouped** headers which contain - * sub headers. - * - * See also {@link Ext.grid.header.Container#getGridColumns} - * @return {Ext.grid.column.Column[]} columns An array of visible columns. Returns - * an empty array if no visible columns are found. - */ - getVisibleGridColumns: function() { - var me = this, - allColumns, rootHeader, result, len, i, column; - if (me.gridVisibleColumns) { - return me.gridVisibleColumns; - } - allColumns = me.getGridColumns(); - rootHeader = me.getRootHeaderCt(); - result = []; - len = allColumns.length; - // Use an inline check instead of ComponentQuery filtering for better performance for - // repeated grid row rendering - as in buffered rendering. - for (i = 0; i < len; i++) { - column = allColumns[i]; - if (!column.hidden && !column.isColumnHidden(rootHeader)) { - result[result.length] = column; - } - } - me.gridVisibleColumns = result; - return result; - }, - isColumnHidden: function(rootHeader) { - var owner = this.getRefOwner(); - while (owner && owner !== rootHeader) { - if (owner.hidden) { - return true; - } - owner = owner.getRefOwner(); - } - return false; - }, - /** - * @method getGridColumns - * Returns an array of all columns which exist in the grid's View, visible or not. - * This goes down to the leaf column header level, and does not return **grouped** - * headers which contain sub headers. - * - * It includes hidden headers even though they are not rendered. This is for - * collection of menu items for the column hide/show menu. - * - * Headers which have a hidden ancestor have a `hiddenAncestor: true` property - * injected so that descendants are known to be hidden without interrogating that - * header's ownerCt axis for a hidden ancestor. - * - * See also {@link Ext.grid.header.Container#getVisibleGridColumns} - * @return {Ext.grid.column.Column[]} columns An array of columns. Returns an - * empty array if no columns are found. - */ - /** @ignore */ - getGridColumns: function(/* private - used in recursion*/ - inResult, hiddenAncestor) { - if (!inResult && this.gridDataColumns) { - return this.gridDataColumns; - } - var me = this, - result = inResult || [], - items, i, len, item, lastVisibleColumn; - hiddenAncestor = hiddenAncestor || me.hidden; - if (me.items) { - items = me.items.items; - // An ActionColumn (Columns extend HeaderContainer) may have an items *array* being the action items that it renders. - if (items) { - for (i = 0 , len = items.length; i < len; i++) { - item = items[i]; - if (item.isGroupHeader) { - // Group headers will need a visibleIndex for if/when they're removed from their owner. - // See Ext.layout.container.Container#moveItemBefore. - item.visibleIndex = result.length; - item.getGridColumns(result, hiddenAncestor); - } else { - item.hiddenAncestor = hiddenAncestor; - result.push(item); - } - } - } - } - if (!inResult) { - me.gridDataColumns = result; - } - // If top level, correct first and last visible column flags - if (!inResult && len) { - // Set firstVisible and lastVisible flags - for (i = 0 , len = result.length; i < len; i++) { - item = result[i]; - // The column index within all (visible AND hidden) leaf level columns. - // Used as the cellIndex in TableView's cell renderer call - item.fullColumnIndex = i; - item.isFirstVisible = item.isLastVisible = false; - if (!(item.hidden || item.hiddenAncestor)) { - if (!lastVisibleColumn) { - item.isFirstVisible = true; - } - lastVisibleColumn = item; - } - } - // If we haven't hidden all columns, tag the last visible one encountered - if (lastVisibleColumn) { - lastVisibleColumn.isLastVisible = true; - } - } - return result; - }, - /** - * @private - * For use by column headers in determining whether there are any hideable columns when deciding whether or not - * the header menu should be disabled. - */ - getHideableColumns: function() { - var me = this, - result = me.hideableColumns; - if (!result) { - result = me.hideableColumns = me.query('[hideable]'); - } - return result; - }, - /** - * Returns the index of a leaf level header regardless of what the nesting - * structure is. - * - * If a group header is passed, the index of the first leaf level header within it is returned. - * - * @param {Ext.grid.column.Column} header The header to find the index of - * @return {Number} The index of the specified column header - */ - getHeaderIndex: function(header) { - // Binding the columnManager to a column makes it backwards-compatible with versions - // that only bind the columnManager to a root header. - if (!this.columnManager) { - this.columnManager = this.getRootHeaderCt().columnManager; - } - return this.columnManager.getHeaderIndex(header); - }, - /** - * Get a leaf level header by index regardless of what the nesting - * structure is. - * - * @param {Number} index The column index for which to retrieve the column. - */ - getHeaderAtIndex: function(index) { - // Binding the columnManager to a column makes it backwards-compatible with versions - // that only bind the columnManager to a root header. - if (!this.columnManager) { - this.columnManager = this.getRootHeaderCt().columnManager; - } - return this.columnManager.getHeaderAtIndex(index); - }, - /** - * When passed a column index, returns the closet *visible* column to that. If the column at the passed index is visible, - * that is returned. If it is hidden, either the next visible, or the previous visible column is returned. - * - * @param {Number} index Position at which to find the closest visible column. - */ - getVisibleHeaderClosestToIndex: function(index) { - // Binding the columnManager to a column makes it backwards-compatible with versions - // that only bind the columnManager to a root header. - if (!this.visibleColumnManager) { - this.visibleColumnManager = this.getRootHeaderCt().visibleColumnManager; - } - return this.visibleColumnManager.getVisibleHeaderClosestToIndex(index); - }, - applyForceFit: function(header) { - var me = this, - view = me.view, - minWidth = Ext.grid.plugin.HeaderResizer.prototype.minColWidth, - // Used when a column's max contents are larger than the available view width. - useMinWidthForFlex = false, - defaultWidth = Ext.grid.header.Container.prototype.defaultWidth, - availFlex = me.el.dom.clientWidth - (view.el.dom.scrollHeight > view.el.dom.clientHeight ? Ext.getScrollbarSize().width : 0), - totalFlex = 0, - items = me.getVisibleGridColumns(), - hidden = header.hidden, - len, i, item, maxAvailFlexOneColumn, myWidth; - function getTotalFlex() { - for (i = 0 , len = items.length; i < len; i++) { - item = items[i]; - // Skip the current header. - if (item === header) { - - continue; - } - item.flex = item.flex || item.width || item.getWidth(); - totalFlex += item.flex; - item.width = null; - } - } - function applyWidth() { - // The currently-sized column (whether resized or reshown) will already - // have a width, so all other columns will need to be flexed. - var isCurrentHeader; - for (i = 0 , len = items.length; i < len; i++) { - item = items[i]; - isCurrentHeader = (item === header); - if (useMinWidthForFlex && !isCurrentHeader) { - // The selected column is extremely large so set all the others as flex minWidth. - item.flex = minWidth; - item.width = null; - } else if (!isCurrentHeader) { - // Note that any widths MUST be converted to flex. Imagine that all but one columns - // are hidden. The widths of each column very easily could be greater than the total - // available width (think about the how visible header widths increase as sibling - // columns are hidden), so they cannot be reliably used to size the header, and the only - // safe approach is to convert any all widths to flex (except for the current header). - myWidth = item.flex || defaultWidth; - item.flex = Math.max(Math.ceil((myWidth / totalFlex) * availFlex), minWidth); - item.width = null; - } - item.setWidth(item.width || item.flex); - } - } - Ext.suspendLayouts(); - // Determine the max amount of flex that a single column can have. - maxAvailFlexOneColumn = (availFlex - ((items.length + 1) * minWidth)); - // First, remove the header's flex as it should always receive a set width - // since it is the header being operated on. - header.flex = null; - if (hidden) { - myWidth = header.width || header.savedWidth; - header.savedWidth = null; - } else { - myWidth = view.getMaxContentWidth(header); - } - // We need to know if the max content width of the selected column would blow out the - // grid. If so, all the other visible columns will be flexed to minWidth. - if (myWidth > maxAvailFlexOneColumn) { - header.width = maxAvailFlexOneColumn; - useMinWidthForFlex = true; - } else { - header.width = myWidth; - // Substract the current header's width from the available flex + some padding - // to ensure that the last column doesn't get nudged out of the view. - availFlex -= myWidth + defaultWidth; - getTotalFlex(); - } - applyWidth(); - Ext.resumeLayouts(true); - }, - autoSizeColumn: function(header) { - var view = this.view; - if (view) { - view.autoSizeColumn(header); - if (this.forceFit) { - this.applyForceFit(header); - } - } - }, - getRefItems: function(deep) { - // Override to include the header menu in the component tree - var result = this.callParent([ - deep - ]); - if (this.menu) { - result.push(this.menu); - } - return result; - }, - privates: { - beginChildHide: function() { - ++this.childHideCount; - }, - endChildHide: function() { - --this.childHideCount; - }, - getFocusables: function() { - return this.isRootHeader ? this.getVisibleGridColumns() : this.items.items; - }, - createFocusableContainerKeyNav: function(el) { - var me = this; - return new Ext.util.KeyNav(el, { - scope: me, - down: me.showHeaderMenu, - left: me.onFocusableContainerLeftKey, - right: me.onFocusableContainerRightKey, - home: me.onHomeKey, - end: me.onEndKey, - space: me.onHeaderActivate, - enter: me.onHeaderActivate - }); - }, - onHomeKey: function(e) { - return this.focusChild(null, true, e); - }, - onEndKey: function(e) { - return this.focusChild(null, false, e); - }, - showHeaderMenu: function(e) { - var column = this.getFocusableFromEvent(e); - // DownArrow event must be from a column, not a Component within the column (eg filter fields) - if (column && column.isColumn && column.triggerEl) { - this.onHeaderTriggerClick(column, e, column.triggerEl); - } - }, - onHeaderActivate: function(e) { - var column = this.getFocusableFromEvent(e), - view, lastFocused; - // Remember that not every descendant of a headerCt is a column! It could be a child component of a column. - if (column && column.isColumn) { - view = column.getView(); - // Sort the column is configured that way. - // sortOnClick may be set to false by SpreadsheelSelectionModel to allow click to select a column. - if (column.sortable && this.sortOnClick) { - lastFocused = view.getNavigationModel().getLastFocused(); - column.toggleSortState(); - // After keyboard sort, bring last focused record into view - if (lastFocused) { - view.ownerCt.ensureVisible(lastFocused.record); - } - } - // onHeaderClick is a necessary part of accessibility processing, sortable or not. - this.onHeaderClick(column, e, column.el); - } - }, - onFocusableContainerMousedown: function(e, target) { - var targetCmp = Ext.Component.fromElement(target); - if (targetCmp === this) { - e.preventDefault(); - } else { - // The DDManager (Header Containers are draggable) prevents mousedown default - // So we must explicitly focus the header - targetCmp.focus(); - } - } - } -}); - -/** - * This class specifies the definition for a column inside a {@link Ext.grid.Panel}. It encompasses - * both the grid header configuration as well as displaying data within the grid itself. If the - * {@link #columns} configuration is specified, this column will become a column group and can - * contain other columns inside. In general, this class will not be created directly, rather - * an array of column configurations will be passed to the grid: - * - * @example - * Ext.create('Ext.data.Store', { - * storeId:'employeeStore', - * fields:['firstname', 'lastname', 'seniority', 'dep', 'hired'], - * data:[ - * {firstname:"Michael", lastname:"Scott", seniority:7, dep:"Management", hired:"01/10/2004"}, - * {firstname:"Dwight", lastname:"Schrute", seniority:2, dep:"Sales", hired:"04/01/2004"}, - * {firstname:"Jim", lastname:"Halpert", seniority:3, dep:"Sales", hired:"02/22/2006"}, - * {firstname:"Kevin", lastname:"Malone", seniority:4, dep:"Accounting", hired:"06/10/2007"}, - * {firstname:"Angela", lastname:"Martin", seniority:5, dep:"Accounting", hired:"10/21/2008"} - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Column Demo', - * store: Ext.data.StoreManager.lookup('employeeStore'), - * columns: [ - * {text: 'First Name', dataIndex:'firstname'}, - * {text: 'Last Name', dataIndex:'lastname'}, - * {text: 'Hired Month', dataIndex:'hired', xtype:'datecolumn', format:'M'}, - * {text: 'Department (Yrs)', xtype:'templatecolumn', tpl:'{dep} ({seniority})'} - * ], - * width: 400, - * forceFit: true, - * renderTo: Ext.getBody() - * }); - * - * # Convenience Subclasses - * - * There are several column subclasses that provide default rendering for various data types - * - * - {@link Ext.grid.column.Action}: Renders icons that can respond to click events inline - * - {@link Ext.grid.column.Boolean}: Renders for boolean values - * - {@link Ext.grid.column.Date}: Renders for date values - * - {@link Ext.grid.column.Number}: Renders for numeric values - * - {@link Ext.grid.column.Template}: Renders a value using an {@link Ext.XTemplate} using the record data - * - * # Setting Sizes - * - * The columns are laid out by a {@link Ext.layout.container.HBox} layout, so a column can either - * be given an explicit width value or a flex configuration. If no width is specified the grid will - * automatically the size the column to 100px. For column groups, the size is calculated by measuring - * the width of the child columns, so a width option should not be specified in that case. - * - * # Header Options - * - * - {@link #text}: Sets the header text for the column - * - {@link #sortable}: Specifies whether the column can be sorted by clicking the header or using the column menu - * - {@link #hideable}: Specifies whether the column can be hidden using the column menu - * - {@link #menuDisabled}: Disables the column header menu - * - {@link #cfg-draggable}: Specifies whether the column header can be reordered by dragging - * - {@link #groupable}: Specifies whether the grid can be grouped by the column dataIndex. See also {@link Ext.grid.feature.Grouping} - * - * # Data Options - * - * - {@link #dataIndex}: The dataIndex is the field in the underlying {@link Ext.data.Store} to use as the value for the column. - * - {@link Ext.grid.column.Column#renderer}: Allows the underlying store - * value to be transformed before being displayed in the grid - * - * ## State saving - * - * When the owning {@link Ext.grid.Panel Grid} is configured - * {@link Ext.grid.Panel#cfg-stateful}, it will save its column state (order and width) - * encapsulated within the default Panel state of changed width and height and - * collapsed/expanded state. - * - * On a `stateful` grid, not only should the Grid have a - * {@link Ext.grid.Panel#cfg-stateId}, each column of the grid should also be configured - * with a {@link #stateId} which identifies that column locally within the grid. - * - * Omitting the `stateId` config from the columns results in columns with generated - * internal ID's. The generated ID's are subject to change on each page load - * making it impossible for the state manager to restore the previous state of the - * columns. - */ -Ext.define('Ext.grid.column.Column', { - extend: 'Ext.grid.header.Container', - xtype: 'gridcolumn', - requires: [ - 'Ext.grid.ColumnComponentLayout', - 'Ext.grid.ColumnLayout', - 'Ext.app.bind.Template' - ], - // for "format" support - alternateClassName: 'Ext.grid.Column', - config: { - triggerVisible: false, - /** - * @cfg {String/Object/Ext.util.Sorter} sorter - * A Sorter, or sorter config object to apply when the standard user interface sort gesture is invoked. - * This is usually clicking this column header, but there are also menu options to sort ascending or descending. - * - * Note that a sorter may also be specified as a function which accepts two records to compare. - */ - sorter: null - }, - // TODO: Implement visible triggers for touch-based platforms. - // Styling will need tweaking - looks a bit ugly with all triggers always visible. - baseCls: Ext.baseCSSPrefix + 'column-header', - // Not the standard, automatically applied overCls because we must filter out overs of child headers. - hoverCls: Ext.baseCSSPrefix + 'column-header-over', - ariaRole: 'columnheader', - enableFocusableContainer: false, - sortState: null, - possibleSortStates: [ - 'ASC', - 'DESC' - ], - // These are not readable descriptions; the values go in the aria-sort attribute. - ariaSortStates: { - ASC: 'ascending', - DESC: 'descending' - }, - childEls: [ - 'titleEl', - 'triggerEl', - 'textEl', - 'textContainerEl' - ], - /** - * @private - * @cfg {Boolean} [headerWrap=false] - * The default setting indicates that external CSS rules dictate that the title is `white-space: nowrap` and - * therefore, width cannot affect the measured height by causing text wrapping. This is what the Sencha-supplied - * styles set. If you change those styles to allow text wrapping, you must set this to `true`. - */ - headerWrap: false, - renderTpl: [ - '', - '{%this.renderContainer(out,values)%}' - ], - /** - * @cfg {Object[]} columns - * An optional array of sub-column definitions. This column becomes a group, and houses the columns defined in the - * `columns` config. - * - * Group columns may not be sortable. But they may be hideable and moveable. And you may move headers into and out - * of a group. Note that if all sub columns are dragged out of a group, the group is destroyed. - */ - /** - * @cfg {String} stateId - * An identifier which identifies this column uniquely within the owning grid's {@link #stateful state}. - * - * This does not have to be *globally* unique. A column's state is not saved standalone. It is encapsulated within - * the owning grid's state. - */ - /** - * @cfg {String} dataIndex - * The name of the field in the grid's {@link Ext.data.Store}'s {@link Ext.data.Model} definition from - * which to draw the column's value. **Required.** - */ - dataIndex: null, - /** - * @cfg {String} text - * The header text to be used as innerHTML (html tags are accepted) to display in the Grid. - * **Note**: to have a clickable header with no text displayed you can use the default of ` ` aka ` `. - */ - text: ' ', - /** - * @cfg {String} header - * The header text. - * @deprecated 4.0 Use {@link #text} instead. - */ - /** - * @cfg {String} menuText - * The text to render in the column visibility selection menu for this column. If not - * specified, will default to the text value. - */ - menuText: null, - /** - * @cfg {String} [emptyCellText=undefined] - * The text to display in empty cells (cells with a value of `undefined`, `null`, or `''`). - * - * Defaults to ` ` aka ` `. - */ - emptyCellText: ' ', - /** - * @cfg {Boolean} sortable - * False to disable sorting of this column. Whether local/remote sorting is used is specified in - * `{@link Ext.data.Store#remoteSort}`. - */ - sortable: true, - /** - * @cfg {Boolean} lockable - * If the grid is configured with {@link Ext.panel.Table#enableLocking enableLocking}, or has columns which are - * configured with a {@link #locked} value, this option may be used to disable user-driven locking or unlocking - * of this column. This column will remain in the side into which its own {@link #locked} configuration placed it. - */ - /** - * @cfg {Boolean} groupable - * If the grid uses a {@link Ext.grid.feature.Grouping}, this option may be used to disable the header menu - * item to group by the column selected. By default, the header menu group option is enabled. Set to false to - * disable (but still show) the group option in the header menu for the column. - */ - /** - * @cfg {Boolean} fixed - * True to prevent the column from being resizable. - * @deprecated 4.0 Use {@link #resizable} instead. - */ - /** - * @cfg {Boolean} [locked=false] - * True to lock this column in place. Implicitly enables locking on the grid. - * See also {@link Ext.grid.Panel#enableLocking}. - */ - /** - * @cfg {Boolean} [cellWrap=false] - * True to allow whitespace in this column's cells to wrap, and cause taller column height where - * necessary. - * - * This implicitly sets the {@link #variableRowHeight} config to `true` - */ - /** - * @cfg {Boolean} [variableRowHeight=false] - * True to indicate that data in this column may take on an unpredictable height, possibly differing from row to row. - * - * If this is set, then View refreshes, and removal and addition of new rows will result in an ExtJS layout of the grid - * in order to adjust for possible addition/removal of scrollbars in the case of data changing height. - * - * This config also tells the View's buffered renderer that row heights are unpredictable, and must be remeasured as the view is refreshed. - */ - /** - * @cfg {Boolean} resizable - * False to prevent the column from being resizable. - */ - resizable: true, - /** - * @cfg {Boolean} hideable - * False to prevent the user from hiding this column. - */ - hideable: true, - /** - * @cfg {Boolean} menuDisabled - * True to disable the column header menu containing sort/hide options. - */ - menuDisabled: false, - /** - * @cfg {Function/String} renderer - * A renderer is an 'interceptor' method which can be used to transform data (value, - * appearance, etc.) before it is rendered. Example: - * - * **NOTE:** In previous releases, a string was treated as a method on - * `Ext.util.Format` but that is now handled by the {@link #formatter} config. - * - * @param {Object} value The data value for the current cell - * - * renderer: function(value){ - * // evaluates `value` to append either `person' or `people` - * return Ext.util.Format.plural(value, 'person', 'people'); - * } - * - * @param {Object} metaData A collection of metadata about the current cell; can be - * used or modified by the renderer. Recognized properties are: `tdCls`, `tdAttr`, - * and `tdStyle`. - * - * To add style attributes to the `<td>` element, you must use the `tdStyle` - * property. Using a style attribute in the `tdAttr` property will override the - * styles the column sets, such as the width which will break the rendering. - * - * You can see an example of using the metaData parameter below. - * - * Ext.create('Ext.data.Store', { - * storeId: 'simpsonsStore', - * fields: ['class', 'attr', 'style'], - * data: { - * 'class': 'red-bg', - * 'attr': 'lightyellow', - * 'style': 'red' - * } - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Simpsons', - * store: Ext.data.StoreManager.lookup('simpsonsStore'), - * columns: [{ - * text: 'Name', - * dataIndex: 'class', - * renderer: function (value, metaData) { - * metaData.tdCls = value; - * return value; - * } - * }, { - * text: 'Email', - * dataIndex: 'attr', - * flex: 1, - * renderer: function (value, metaData) { - * metaData.tdAttr = 'bgcolor="' + value + '"'; - * return value; - * } - * }, { - * text: 'Phone', - * dataIndex: 'style', - * renderer: function (value, metaData) { - * metaData.tdStyle = 'color:' + value; - * return value; - * } - * }], - * height: 200, - * width: 400, - * renderTo: Ext.getBody() - * }); - * - * @param {Ext.data.Model} record The record for the current row - * - * renderer: function (value, metaData, record) { - * // evaluate the record's `updated` field and if truthy return the value - * // from the `newVal` field, else return value - * var updated = record.get('updated'); - * return updated ? record.get('newVal') : value; - * } - * - * @param {Number} rowIndex The index of the current row - * - * renderer: function (value, metaData, record, rowIndex) { - * // style the cell differently for even / odd values - * var odd = (rowIndex % 2 === 0); - * metaData.tdStyle = 'color:' + (odd ? 'gray' : 'red'); - * } - * - * @param {Number} colIndex The index of the current column - * - * var myRenderer = function(value, metaData, record, rowIndex, colIndex) { - * if (colIndex === 0) { - * metaData.tdAttr = 'data-qtip=' + value; - * } - * // additional logic to apply to values in all columns - * return value; - * } - * - * // using the same renderer on all columns you can process the value for - * // each column with the same logic and only set a tooltip on the first column - * renderer: myRenderer - * - * _See also {@link Ext.tip.QuickTipManager}_ - * - * @param {Ext.data.Store} store The data store - * - * renderer: function (value, metaData, record, rowIndex, colIndex, store) { - * // style the cell differently depending on how the value relates to the - * // average of all values - * var average = store.average('grades'); - * metaData.tdCls = (value < average) ? 'needsImprovement' : 'satisfactory'; - * return value; - * } - * - * @param {Ext.view.View} view The data view - * - * renderer: function (value, metaData, record, rowIndex, colIndex, store, view) { - * // style the cell using the dataIndex of the column - * var headerCt = this.getHeaderContainer(), - * column = headerCt.getHeaderAtIndex(colIndex); - * - * metaData.tdCls = 'app-' + column.dataIndex; - * return value; - * } - * - * @return {String} - * The HTML string to be rendered. - * @declarativeHandler - */ - renderer: false, - /** - * @cfg {Function/String} updater - * An updater is a method which is used when records are updated, and an *existing* grid row needs updating. - * The method is passed the cell element and may manipulate it in any way. - * - * **Note**: The updater is required to insert the {@link #emptyCellText} if there - * is no value in the cell. - * - * Ext.create('Ext.grid.Panel', { - * title: 'Grades', - * store: { - * fields: ['originalScore', 'newScore'], - * data: [{ - * originalScore: 70, - * newScore: 70 - * }] - * }, - * columns: [{ - * text: 'Score', - * dataIndex: 'newScore', - * editor: 'numberfield', - * flex: 1, - * updater: function (cell, value, record, view) { - * var inner = Ext.get(cell).first(), - * originalScore = record.get('originalScore'), - * color = (value === originalScore) ? 'black' : (value > originalScore) ? 'green' : 'red'; - * - * // set the color based on the current value relative to the originalScore value - * // * same = black - * // * higher = green - * // * less = red - * inner.applyStyles({ - * color: color - * }); - * // pass the value to the cell's inner el - * inner.setHtml(value); - * } - * }], - * height: 200, - * width: 400, - * renderTo: document.body, - * tbar: [{ - * xtype: 'numberfield', - * fieldLabel: 'New Score', - * value: 70, - * listeners: { - * change: function (field, newValue) { - * this.up('grid').getStore().first().set('newScore', newValue); - * } - * } - * }] - * }); - * - * If a string is passed it is assumed to be the name of a method defined by the - * {@link #method-getController ViewController} or an ancestor component configured as {@link #defaultListenerScope}. - * @cfg {HTMLElement} updater.cell The HTML cell element to update. - * @cfg {Object} updater.value The data value for the current cell - * @cfg {Ext.data.Model} updater.record The record for the current row - * @cfg {Ext.view.View} updater.view The current view - * - * **Note**: The updater is required to insert the {@link #emptyCellText} if there is no value in the cell. - * - * @declarativeHandler - */ - /** - * @cfg {Object} scope - * The scope to use when calling the - * {@link Ext.grid.column.Column#renderer} function. - */ - /** - * @method defaultRenderer - * When defined this will take precedence over the - * {@link Ext.grid.column.Column#renderer renderer} config. - * This is meant to be defined in subclasses that wish to supply their own renderer. - * @protected - * @template - */ - /** - * @cfg {Function/String} editRenderer - * A renderer to be used in conjunction with - * {@link Ext.grid.plugin.RowEditing RowEditing}. This renderer is used to display a - * custom value for non-editable fields. - * - * **Note:** The editRenderer is called when the roweditor is initially shown. - * Changes to the record during editing will not call editRenderer. - * - * var store = Ext.create('Ext.data.Store', { - * fields: ['name', 'email'], - * data: [{ - * "name": "Finn", - * "email": "finn@adventuretime.com" - * }, { - * "name": "Jake", - * "email": "jake@adventuretime.com" - * }] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Land Of Ooo', - * store: store, - * columns: [{ - * text: 'Name', - * dataIndex: 'name', - * editRenderer: function(value){ - * return '' + value + ''; - * } - * }, { - * text: 'Email', - * dataIndex: 'email', - * flex: 1, - * editor: { - * xtype: 'textfield', - * allowBlank: false - * } - * }], - * plugins: { - * ptype: 'rowediting', - * clicksToEdit: 1 - * }, - * height: 200, - * width: 400, - * renderTo: document.body - * }); - * - * @param {Object} value The data value for the current cell - * - * editRenderer: function(value){ - * // evaluates `value` to append either `person' or `people` - * return Ext.util.Format.plural(value, 'person', 'people'); - * } - * - * @param {Object} metaData **Note:** The metadata param is passed to the - * editRenderer, but is not used. - * - * @param {Ext.data.Model} record The record for the current row - * - * editRenderer: function (value, metaData, record) { - * // evaluate the record's `updated` field and if truthy return the value - * // from the `newVal` field, else return value - * var updated = record.get('updated'); - * return updated ? record.get('newVal') : value; - * } - * - * @param {Number} rowIndex The index of the current row - * - * editRenderer: function (value, metaData, record, rowIndex) { - * // style the value differently for even / odd values - * var odd = (rowIndex % 2 === 0), - * color = (odd ? 'gray' : 'red'); - * return '' + value + ''; - * } - * - * @param {Number} colIndex The index of the current column - * - * @param {Ext.data.Store} store The data store - * - * editRenderer: function (value, metaData, record, rowIndex, colIndex, store) { - * // style the cell differently depending on how the value relates to the - * // average of all values - * var average = store.average('grades'), - * status = (value < average) ? 'needsImprovement' : 'satisfactory'; - * return '' + value + ''; - * } - * - * @param {Ext.view.View} view The data view - * - * editRenderer: function (value, metaData, record, rowIndex, colIndex, store, view) { - * // style the value using the dataIndex of the column - * var headerCt = this.getHeaderContainer(), - * column = headerCt.getHeaderAtIndex(colIndex); - * - * return '' + value + ''; - * } - * - * @return {String} - * The HTML string to be rendered. - * @declarativeHandler - */ - /** - * @cfg {Function/String} summaryRenderer - * A renderer to be used in conjunction with the {@link Ext.grid.feature.Summary Summary} or - * {@link Ext.grid.feature.GroupingSummary GroupingSummary} features. This renderer is used to - * display a summary value for this column. - * @declarativeHandler - */ - /** - * @cfg {String} align - * Sets the alignment of the header and rendered columns. - * Possible values are: `'left'`, `'center'`, and `'right'`. - */ - align: 'left', - /** - * @cfg {Boolean} draggable - * False to disable drag-drop reordering of this column. - */ - draggable: true, - /** - * @cfg {String} tooltip - * A tooltip to display for this column header - */ - /** - * @cfg {String} [tooltipType="qtip"] - * The type of {@link #tooltip} to use. Either 'qtip' for QuickTips or 'title' for title attribute. - */ - tooltipType: 'qtip', - // Header does not use the typical ComponentDraggable class and therefore we - // override this with an emptyFn. It is controlled at the HeaderDragZone. - initDraggable: Ext.emptyFn, - /** - * @cfg {String} tdCls - * A CSS class names to apply to the table cells for this column. - */ - tdCls: '', - /** - * @cfg {Object/String} editor - * An optional xtype or config object for a {@link Ext.form.field.Field Field} to use for editing. - * Only applicable if the grid is using an {@link Ext.grid.plugin.Editing Editing} plugin. - */ - /** - * @cfg {Object/String} field - * Alias for {@link #editor}. - * @deprecated 4.0.5 Use {@link #editor} instead. - */ - /** - * @cfg {Boolean} producesHTML - * This flag indicates that the renderer produces HTML. - * - * If this column is going to be updated rapidly, and the - * {@link Ext.grid.column.Column#renderer} or {@link #cfg-updater} only produces - * text, then to avoid the expense of HTML parsing and element production during the - * update, this property may be configured as `false`. - */ - producesHTML: true, - /** - * @cfg {Boolean} ignoreExport - * This flag indicates that this column will be ignored when grid data is exported. - * - * When grid data is exported you may want to export only some columns that are important - * and not everything. Widget, check and action columns are not relevant when data is - * exported. You can set this flag on any column that you want to be ignored during export. - * - * This is used by {@link Ext.grid.plugin.Clipboard clipboard plugin} and {@link Ext.grid.plugin.Exporter exporter plugin}. - */ - ignoreExport: false, - /** - * @property {Ext.dom.Element} triggerEl - * Element that acts as button for column header dropdown menu. - */ - /** - * @property {Ext.dom.Element} textEl - * Element that contains the text in column header. - */ - /** - * @property {Boolean} isHeader - * @deprecated see isColumn - * Set in this class to identify, at runtime, instances which are not instances of the - * HeaderContainer base class, but are in fact, the subclass: Header. - */ - isHeader: true, - /** - * @property {Boolean} isColumn - * @readonly - * Set in this class to identify, at runtime, instances which are not instances of the - * HeaderContainer base class, but are in fact simple column headers. - */ - isColumn: true, - tabIndex: -1, - ascSortCls: Ext.baseCSSPrefix + 'column-header-sort-ASC', - descSortCls: Ext.baseCSSPrefix + 'column-header-sort-DESC', - componentLayout: 'columncomponent', - groupSubHeaderCls: Ext.baseCSSPrefix + 'group-sub-header', - groupHeaderCls: Ext.baseCSSPrefix + 'group-header', - clickTargetName: 'titleEl', - // So that when removing from group headers which are then empty and then get destroyed, there's no child DOM left - detachOnRemove: true, - // We need to override the default component resizable behaviour here - initResizable: Ext.emptyFn, - // Property names to reference the different types of renderers and formatters that - // we can use. - rendererNames: { - column: 'renderer', - edit: 'editRenderer', - summary: 'summaryRenderer' - }, - formatterNames: { - column: 'formatter', - edit: 'editFormatter', - summary: 'summaryFormatter' - }, - initComponent: function() { - var me = this; - // Preserve the scope to resolve a custom renderer. - // Subclasses (TreeColumn) may insist on scope being this. - if (!me.rendererScope) { - me.rendererScope = me.scope; - } - if (me.header != null) { - me.text = me.header; - me.header = null; - } - if (me.cellWrap) { - me.tdCls = (me.tdCls || '') + ' ' + Ext.baseCSSPrefix + 'wrap-cell'; - } - // A group header; It contains items which are themselves Headers - if (me.columns != null) { - me.isGroupHeader = true; - me.ariaRole = 'presentation'; - if (me.dataIndex) { - Ext.raise('Ext.grid.column.Column: Group header may not accept a dataIndex'); - } - if ((me.width && me.width !== Ext.grid.header.Container.prototype.defaultWidth) || me.flex) { - Ext.raise('Ext.grid.column.Column: Group header does not support setting explicit widths or flexs. The group header width is calculated by the sum of its children.'); - } - // The headers become child items - me.items = me.columns; - me.columns = me.flex = me.width = null; - me.cls = (me.cls || '') + ' ' + me.groupHeaderCls; - // A group cannot be sorted, or resized - it shrinkwraps its children - me.sortable = me.resizable = false; - me.align = 'center'; - } else { - // Flexed Headers need to have a minWidth defined so that they can never be squeezed out of existence by the - // HeaderContainer's specialized Box layout, the ColumnLayout. The ColumnLayout's overridden calculateChildboxes - // method extends the available layout space to accommodate the "desiredWidth" of all the columns. - if (me.flex) { - me.minWidth = me.minWidth || Ext.grid.plugin.HeaderResizer.prototype.minColWidth; - } - } - me.addCls(Ext.baseCSSPrefix + 'column-header-align-' + me.align); - // Set up the renderer types: 'renderer', 'editRenderer', and 'summaryRenderer' - me.setupRenderer(); - me.setupRenderer('edit'); - me.setupRenderer('summary'); - // Initialize as a HeaderContainer - me.callParent(arguments); - }, - onAdded: function(container, pos, instanced) { - var me = this, - sorter, ownerGrid, counterOwner; - me.callParent([ - container, - pos, - instanced - ]); - if (!me.headerId) { - // Sequential header counter MUST be based on the top level grid to avoid duplicates from sides - // of a lockable assembly. - ownerGrid = me.up('tablepanel'); - counterOwner = ownerGrid ? ownerGrid.ownerGrid : me.getRootHeaderCt(); - counterOwner.headerCounter = (counterOwner.headerCounter || 0) + 1; - me.headerId = 'h' + counterOwner.headerCounter; - } - // MUST stamp a stateId into this objec; state application relies on reading the property, NOT using the getter! - // Only generate a stateId if it really needs one. - if (!me.stateId) { - // This was the headerId generated in 4.0, so to preserve saved state, we now - // assign a default stateId in that same manner. The stateId's of a column are - // not global at the stateProvider, but are local to the grid state data. The - // headerId should still follow our standard naming convention. - me.stateId = me.initialConfig.id || me.headerId; - } - sorter = me.getSorter(); - if (sorter && !sorter.initialConfig.id) { - sorter.setId((me.dataIndex || me.stateId) + '-sorter'); - } - }, - applySorter: function(sorter) { - // Have the sorter spec decoded by the collection that will host it. - return this.getRootHeaderCt().up('tablepanel').store.getData().getSorters().decodeSorter(sorter); - }, - bindFormatter: function(format) { - var me = this; - return function(v) { - return format.format(v, format.scope || me.rendererScope || me.resolveListenerScope()); - }; - }, - bindRenderer: function(renderer) { - var me = this; - if (renderer in Ext.util.Format) { - Ext.log.warn('Use "formatter" config instead of "renderer" to use ' + 'Ext.util.Format to format cell values'); - } - me.hasCustomRenderer = true; - return function() { - return Ext.callback(renderer, me.rendererScope, arguments, 0, me); - }; - }, - setupRenderer: function(type) { - // type can be null or 'edit', or 'summary' - type = type || 'column'; - var me = this, - format = me[me.formatterNames[type]], - renderer = me[me.rendererNames[type]], - isColumnRenderer = type === 'column', - scoped, dynamic; - if (!format) { - if (renderer) { - // Resolve a string renderer into the correct property: 'renderer', 'editRenderer', or 'summaryRenderer' - if (typeof renderer === 'string') { - renderer = me[me.rendererNames[type]] = me.bindRenderer(renderer); - dynamic = true; - } - if (isColumnRenderer) { - // If we are setting up a normal column renderer, detect if it's a custom one (reads more than one parameter) - // We can't read the arg list until we resolve the scope, so we must assume - // it's a renderer that needs a full update if it's dynamic - me.hasCustomRenderer = dynamic || renderer.length > 1; - } - } - // Column renderer could not be resolved: use the default one. - else if (isColumnRenderer && me.defaultRenderer) { - me.renderer = me.defaultRenderer; - me.usingDefaultRenderer = true; - } - } else { - scoped = format.indexOf('this.') === 0; - if (scoped) { - format = format.substring(5); - } - /** - * @cfg {String} formatter - * This config accepts a format specification as would be used in a `Ext.Template` - * formatted token. For example `'round(2)'` to round numbers to 2 decimal places - * or `'date("Y-m-d")'` to format a Date. - * - * In previous releases the `renderer` config had limited abilities to use one - * of the `Ext.util.Format` methods but `formatter` now replaces that usage and - * can also handle formatting parameters. - * - * When the value begins with `"this."` (for example, `"this.foo(2)"`), the - * implied scope on which "foo" is found is the `scope` config for the column. - * - * If the `scope` is not given, or implied using a prefix of `"this"`, then either the - * {@link #method-getController ViewController} or the closest ancestor component configured - * as {@link #defaultListenerScope} is assumed to be the object with the method. - * @since 5.0.0 - */ - format = Ext.app.bind.Template.prototype.parseFormat(format); - me[me.formatterNames[type]] = null; - // processed - trees come back here to add its renderer - if (scoped) { - format.scope = null; - } - // not Ext.util.Format - else if (!Ext.util.Format[format.fmt]) { - Ext.raise('Invalid formatter specified: "' + format.fmt + '"'); - } - // Set up the correct property: 'renderer', 'editRenderer', or 'summaryRenderer' - me[me.rendererNames[type]] = me.bindFormatter(format); - } - }, - getView: function() { - var rootHeaderCt = this.getRootHeaderCt(); - if (rootHeaderCt) { - return rootHeaderCt.view; - } - }, - onFocusLeave: function(e) { - this.callParent([ - e - ]); - if (this.activeMenu) { - this.activeMenu.hide(); - } - }, - initItems: function() { - var me = this; - me.callParent(arguments); - if (me.isGroupHeader) { - // We need to hide the groupheader straightaway if it's configured as hidden or all its children are. - if (me.config.hidden || !me.hasVisibleChildColumns()) { - me.hide(); - } - } - }, - hasVisibleChildColumns: function() { - var items = this.items.items, - len = items.length, - i, item; - for (i = 0; i < len; ++i) { - item = items[i]; - if (item.isColumn && !item.hidden) { - return true; - } - } - return false; - }, - onAdd: function(child) { - var me = this; - if (child.isColumn) { - child.isSubHeader = true; - child.addCls(me.groupSubHeaderCls); - } - if (me.isGroupHeader && me.hidden && me.hasVisibleChildColumns()) { - me.show(); - } - me.callParent([ - child - ]); - }, - onRemove: function(child, isDestroying) { - var me = this; - if (child.isSubHeader) { - child.isSubHeader = false; - child.removeCls(me.groupSubHeaderCls); - } - me.callParent([ - child, - isDestroying - ]); - // By this point, the component will be removed from the items collection. - // - // Note that we don't want to remove any grouped headers that have a descendant that is currently the drag target of an even lower stacked - // grouped header. See the comments in Ext.grid.header.Container#isNested. - if (!(me.destroyed || me.destroying) && !me.hasVisibleChildColumns() && !me.ownerCt.isNested()) { - me.hide(); - } - }, - initRenderData: function() { - var me = this, - tipMarkup = '', - tip = me.tooltip, - text = me.text, - attr = me.tooltipType === 'qtip' ? 'data-qtip' : 'title'; - if (!Ext.isEmpty(tip)) { - tipMarkup = attr + '="' + tip + '" '; - } - return Ext.applyIf(me.callParent(arguments), { - text: text, - empty: text === ' ' || text === ' ' || text === '', - menuDisabled: me.menuDisabled, - tipMarkup: tipMarkup, - triggerStyle: this.getTriggerVisible() ? 'display:block' : '' - }); - }, - applyColumnState: function(state, storeState) { - var me = this, - sorter = me.getSorter(), - stateSorters = storeState && storeState.sorters, - len, i, savedSorter, mySorterId; - // If we have been configured with a sorter, then there SHOULD be a sorter config - // in the storeState with a corresponding ID from which we must restore our sorter's state. - // (The only state we can restore is direction). - // Then we replace the state entry with the real sorter. We MUST do this because the sorter - // is likely to have a custom sortFn. - if (sorter && stateSorters && (len = stateSorters.length)) { - mySorterId = sorter.getId(); - for (i = 0; !savedSorter && i < len; i++) { - if (stateSorters[i].id === mySorterId) { - sorter.setDirection(stateSorters[i].direction); - stateSorters[i] = sorter; - break; - } - } - } - // apply any columns - me.applyColumnsState(state.columns); - // Only state properties which were saved should be restored. - // (Only user-changed properties were saved by getState) - if (state.hidden != null) { - me.hidden = state.hidden; - } - if (state.locked != null) { - me.locked = state.locked; - } - if (state.sortable != null) { - me.sortable = state.sortable; - } - if (state.width != null) { - me.flex = null; - me.width = state.width; - } else if (state.flex != null) { - me.width = null; - me.flex = state.flex; - } - }, - getColumnState: function() { - var me = this, - items = me.items.items, - // Check for the existence of items, since column.Action won't have them - iLen = items ? items.length : 0, - i, - columns = [], - state = { - id: me.getStateId() - }; - me.savePropsToState([ - 'hidden', - 'sortable', - 'locked', - 'flex', - 'width' - ], state); - if (me.isGroupHeader) { - for (i = 0; i < iLen; i++) { - columns.push(items[i].getColumnState()); - } - if (columns.length) { - state.columns = columns; - } - } - if ('width' in state) { - delete state.flex; - } - // width wins - return state; - }, - /** - * Sets the header text for this Column. - * @param {String} text The header to display on this Column. - */ - setText: function(text) { - this.text = text; - if (this.rendered) { - this.textEl.setHtml(text); - } - }, - /** - * Returns the index of this column only if this column is a base level Column. If it - * is a group column, it returns `false`. - * @return {Number} - */ - getIndex: function() { - return this.isGroupColumn ? false : this.getRootHeaderCt().getHeaderIndex(this); - }, - /** - * Returns the index of this column in the list of *visible* columns only if this column is a base level Column. If it - * is a group column, it returns `false`. - * @return {Number} - */ - getVisibleIndex: function() { - // Note that the visibleIndex property is assigned by the owning HeaderContainer - // when assembling the visible column set for the view. - return this.visibleIndex != null ? this.visibleIndex : this.isGroupColumn ? false : Ext.Array.indexOf(this.getRootHeaderCt().getVisibleGridColumns(), this); - }, - getLabelChain: function() { - var child = this, - labels = [], - parent; - while (parent = child.up('headercontainer')) { - if (parent.text) { - labels.unshift(Ext.util.Format.stripTags(parent.text)); - } - child = parent; - } - return labels; - }, - beforeRender: function() { - var me = this, - rootHeaderCt = me.getRootHeaderCt(), - isSortable = me.isSortable(), - labels = [], - ariaAttr; - me.callParent(); - // Disable the menu if there's nothing to show in the menu, ie: - // Column cannot be sorted, grouped or locked, and there are no grid columns which may be hidden - if (!isSortable && !me.groupable && !me.lockable && (rootHeaderCt.grid.enableColumnHide === false || !rootHeaderCt.getHideableColumns().length)) { - me.menuDisabled = true; - } - // Wrapping text may cause unpredictable line heights. - // variableRowHeight is interrogated by the View for all visible columns to determine whether - // addition of new rows should cause an ExtJS layout. - // The View's summation of the presence of visible variableRowHeight columns is also used by - // any buffered renderer to determine how row height should be calculated when determining scroll range. - if (me.cellWrap) { - me.variableRowHeight = true; - } - ariaAttr = me.ariaRenderAttributes || (me.ariaRenderAttributes = {}); - // Ext JS does not support editable column headers - ariaAttr['aria-readonly'] = true; - if (isSortable) { - ariaAttr['aria-sort'] = me.ariaSortStates[me.sortState]; - } - if (me.isSubHeader) { - labels = me.getLabelChain(); - if (me.text) { - labels.push(Ext.util.Format.stripTags(me.text)); - } - if (labels.length) { - ariaAttr['aria-label'] = labels.join(' '); - } - } - me.protoEl.unselectable(); - }, - getTriggerElWidth: function() { - var me = this, - triggerEl = me.triggerEl, - width = me.self.triggerElWidth; - if (triggerEl && width === undefined) { - triggerEl.setStyle('display', 'block'); - width = me.self.triggerElWidth = triggerEl.getWidth(); - triggerEl.setStyle('display', ''); - } - return width; - }, - afterComponentLayout: function(width, height, oldWidth, oldHeight) { - var me = this, - rootHeaderCt = me.getRootHeaderCt(); - me.callParent(arguments); - if (rootHeaderCt && (oldWidth != null || me.flex) && width !== oldWidth) { - rootHeaderCt.onHeaderResize(me, width); - } - }, - onDestroy: function() { - var me = this; - // force destroy on the textEl, IE reports a leak - Ext.destroy(me.field); - me.field = null; - me.callParent(arguments); - }, - onTitleMouseOver: function() { - this.titleEl.addCls(this.hoverCls); - }, - onTitleMouseOut: function() { - this.titleEl.removeCls(this.hoverCls); - }, - onDownKey: function(e) { - if (this.triggerEl) { - this.onTitleElClick(e, this.triggerEl.dom || this.el.dom); - } - }, - onEnterKey: function(e) { - this.onTitleElClick(e, this.el.dom); - }, - /** - * @private - * Double click handler which, if on left or right edges, auto-sizes the column to the left. - * @param e The dblclick event - */ - onTitleElDblClick: function(e) { - var me = this, - prev, leafColumns, headerCt; - // On left edge, resize previous *leaf* column in the grid - if (me.isAtStartEdge(e)) { - // Look for the previous visible column header which is a leaf - // Note: previousNode can walk out of the container (this may be first child of a group) - prev = me.previousNode('gridcolumn:not([hidden]):not([isGroupHeader])'); - // If found in the same grid, auto-size it - if (prev && prev.getRootHeaderCt() === me.getRootHeaderCt()) { - prev.autoSize(); - } - } - // On right edge, resize this column, or last sub-column within it - else if (me.isAtEndEdge(e)) { - // Click on right but in child container - auto-size last leaf column - if (me.isGroupHeader && e.getPoint().isContainedBy(me.layout.innerCt)) { - leafColumns = me.query('gridcolumn:not([hidden]):not([isGroupHeader])'); - me.getRootHeaderCt().autoSizeColumn(leafColumns[leafColumns.length - 1]); - return; - } else { - headerCt = me.getRootHeaderCt(); - // Cannot resize the only column in a forceFit grid. - if (headerCt.visibleColumnManager.getColumns().length === 1 && headerCt.forceFit) { - return; - } - } - me.autoSize(); - } - }, - /** - * Sizes this Column to fit the max content width. - * *Note that group columns shrink-wrap around the size of leaf columns. Auto sizing - * a group column auto-sizes descendant leaf columns.* - */ - autoSize: function() { - var me = this, - leafColumns, numLeaves, i, headerCt; - // Group headers are shrinkwrap width, so auto-sizing one means auto-sizing leaf - // descendants. - if (me.isGroupHeader) { - leafColumns = me.query('gridcolumn:not([hidden]):not([isGroupHeader])'); - numLeaves = leafColumns.length; - headerCt = me.getRootHeaderCt(); - Ext.suspendLayouts(); - for (i = 0; i < numLeaves; i++) { - headerCt.autoSizeColumn(leafColumns[i]); - } - Ext.resumeLayouts(true); - return; - } - me.getRootHeaderCt().autoSizeColumn(me); - }, - onTitleElClick: function(e, t, sortOnClick) { - var me = this, - activeHeader, prevSibling; - // Tap on the resize zone triggers the menu - if (e.pointerType === 'touch') { - prevSibling = me.previousSibling(':not([hidden])'); - // Tap on right edge, activate this header - if (!me.menuDisabled && me.isAtEndEdge(e, parseInt(me.triggerEl.getStyle('width'), 10))) { - if (!me.menuDisabled) { - activeHeader = me; - } - } - // Tap on left edge, activate previous header - else if (prevSibling && !prevSibling.menuDisabled && me.isAtStartEdge(e)) { - activeHeader = prevSibling; - } - } else { - // Firefox doesn't check the current target in a within check. - // Therefore we check the target directly and then within (ancestors) - activeHeader = me.triggerEl && (e.target === me.triggerEl.dom || t === me.triggerEl || e.within(me.triggerEl)) ? me : null; - } - // If it's not a click on the trigger or extreme edges. Or if we are called from a key handler, sort this column. - if (sortOnClick !== false && (!activeHeader && !me.isAtStartEdge(e) && !me.isAtEndEdge(e) || e.getKey())) { - me.toggleSortState(); - } - return activeHeader; - }, - /** - * @private - * Process UI events from the view. The owning TablePanel calls this method, relaying events from the TableView - * @param {String} type Event type, eg 'click' - * @param {Ext.view.Table} view TableView Component - * @param {HTMLElement} cell Cell HTMLElement the event took place within - * @param {Number} recordIndex Index of the associated Store Model (-1 if none) - * @param {Number} cellIndex Cell index within the row - * @param {Ext.event.Event} e Original event - */ - processEvent: function(type, view, cell, recordIndex, cellIndex, e) { - return this.fireEvent.apply(this, arguments); - }, - isSortable: function() { - var rootHeader = this.getRootHeaderCt(), - grid = rootHeader ? rootHeader.grid : null, - sortable = this.sortable; - if (grid && grid.sortableColumns === false) { - sortable = false; - } - return sortable; - }, - toggleSortState: function() { - if (this.isSortable()) { - this.sort(); - } - }, - sort: function(direction) { - var me = this, - grid = me.up('tablepanel'), - store = grid.store, - sorter = me.getSorter(); - // Maintain backward compatibility. - // If the grid is NOT configured with multi column sorting, then specify "replace". - // Only if we are doing multi column sorting do we insert it as one of a multi set. - // Suspend layouts in case multiple views depend upon this grid's store (eg lockable assemblies) - Ext.suspendLayouts(); - me.sorting = true; - if (sorter) { - if (direction) { - sorter.setDirection(direction); - } - store.sort(sorter, grid.multiColumnSort ? 'multi' : 'replace'); - } else { - store.sort(me.getSortParam(), direction, grid.multiColumnSort ? 'multi' : 'replace'); - } - delete me.sorting; - Ext.resumeLayouts(true); - }, - /** - * Returns the parameter to sort upon when sorting this header. By default this returns the dataIndex and will not - * need to be overridden in most cases. - * @return {String} - */ - getSortParam: function() { - return this.dataIndex; - }, - setSortState: function(sorter) { - // Set the UI state to reflect the state of any passed Sorter - // Called by the grid's HeaderContainer on view refresh - var me = this, - direction = sorter && sorter.getDirection(), - ascCls = me.ascSortCls, - descCls = me.descSortCls, - rootHeaderCt = me.getRootHeaderCt(), - ariaDom = me.ariaEl.dom, - changed; - switch (direction) { - case 'DESC': - if (!me.hasCls(descCls)) { - me.addCls(descCls); - me.sortState = 'DESC'; - changed = true; - }; - me.removeCls(ascCls); - break; - case 'ASC': - if (!me.hasCls(ascCls)) { - me.addCls(ascCls); - me.sortState = 'ASC'; - changed = true; - }; - me.removeCls(descCls); - break; - default: - me.removeCls([ - ascCls, - descCls - ]); - me.sortState = null; - break; - } - if (ariaDom) { - if (me.sortState) { - ariaDom.setAttribute('aria-sort', me.ariaSortStates[me.sortState]); - } else { - ariaDom.removeAttribute('aria-sort'); - } - } - // we only want to fire the event if we have actually sorted - if (changed) { - rootHeaderCt.fireEvent('sortchange', rootHeaderCt, me, direction); - } - }, - /** - * Determines whether the UI should be allowed to offer an option to hide this column. - * - * A column may *not* be hidden if to do so would leave the grid with no visible columns. - * - * This is used to determine the enabled/disabled state of header hide menu items. - */ - isHideable: function() { - var result = { - hideCandidate: this, - result: this.hideable - }; - if (result.result) { - this.ownerCt.bubble(this.hasOtherMenuEnabledChildren, null, [ - result - ]); - } - return result.result; - }, - hasOtherMenuEnabledChildren: function(result) { - // Private bubble function used in determining whether this column is hideable. - // Executes in the scope of each component in the bubble sequence - var visibleChildren, count; - // If we've bubbled out the top of the topmost HeaderContainer without finding a level with at least one visible, - // menu-enabled child *which is not the hideCandidate*, no hide! - if (!this.isXType('headercontainer')) { - result.result = false; - return false; - } - // If we find an ancestor level with at least one visible, menu-enabled child - // *which is not the hideCandidate*, then the hideCandidate is hideable. - // Note that we are not using CQ #id matchers - ':not(#' + result.hideCandidate.id + ')' - to exclude - // the hideCandidate because CQ queries are cached for the document's lifetime. - visibleChildren = this.query('>gridcolumn:not([hidden]):not([menuDisabled])'); - count = visibleChildren.length; - if (Ext.Array.contains(visibleChildren, result.hideCandidate)) { - count--; - } - if (count) { - return false; - } - // If we go up, it's because the hideCandidate was the only hideable child, so *this* becomes the hide candidate. - result.hideCandidate = this; - }, - /** - * Determines whether the UI should be allowed to offer an option to lock or unlock this column. Note - * that this includes dragging a column into the opposite side of a {@link Ext.panel.Table#enableLocking lockable} grid. - * - * A column may *not* be moved from one side to the other of a {@link Ext.panel.Table#enableLocking lockable} grid - * if to do so would leave one side with no visible columns. - * - * This is used to determine the enabled/disabled state of the lock/unlock - * menu item used in {@link Ext.panel.Table#enableLocking lockable} grids, and to determine droppabilty when dragging a header. - */ - isLockable: function() { - var result = { - result: this.lockable !== false - }; - if (result.result) { - this.ownerCt.bubble(this.hasMultipleVisibleChildren, null, [ - result - ]); - } - return result.result; - }, - /** - * Determines whether this column is in the locked side of a grid. It may be a descendant node of a locked column - * and as such will *not* have the {@link #locked} flag set. - */ - isLocked: function() { - return this.locked || !!this.up('[isColumn][locked]', '[isRootHeader]'); - }, - hasMultipleVisibleChildren: function(result) { - // Private bubble function used in determining whether this column is lockable. - // Executes in the scope of each component in the bubble sequence - // If we've bubbled out the top of the topmost HeaderContainer without finding a level with more than one visible child, no hide! - if (!this.isXType('headercontainer')) { - result.result = false; - return false; - } - // If we find an ancestor level with more than one visible child, it's fine to hide - if (this.query('>gridcolumn:not([hidden])').length > 1) { - return false; - } - }, - hide: function() { - var me = this, - rootHeaderCt = me.getRootHeaderCt(), - owner = me.getRefOwner(); - // During object construction, so just set the hidden flag and jump out - if (owner.constructing) { - me.callParent(); - return me; - } - if (me.rendered && !me.isVisible()) { - // Already hidden - return me; - } - // Save our last shown width so we can gain space when shown back into fully flexed HeaderContainer. - // If we are, say, flex: 1 and all others are fixed width, then removing will do a layout which will - // convert all widths to flexes which will mean this flex value is too small. - if (rootHeaderCt.forceFit) { - me.visibleSiblingCount = rootHeaderCt.getVisibleGridColumns().length - 1; - if (me.flex) { - me.savedWidth = me.getWidth(); - me.flex = null; - } - } - rootHeaderCt.beginChildHide(); - Ext.suspendLayouts(); - // owner is a group, hide call didn't come from the owner - if (owner.isGroupHeader) { - // The owner only has one item that isn't hidden and it's me; hide the owner. - if (me.isNestedGroupHeader()) { - owner.hide(); - } - if (me.isSubHeader && !me.isGroupHeader && owner.query('>gridcolumn:not([hidden])').length === 1) { - owner.lastHiddenHeader = me; - } - } - me.callParent(); - // Notify owning HeaderContainer. Will trigger a layout and a view refresh. - rootHeaderCt.endChildHide(); - rootHeaderCt.onHeaderHide(me); - Ext.resumeLayouts(true); - return me; - }, - show: function() { - var me = this, - rootHeaderCt = me.getRootHeaderCt(), - ownerCt = me.getRefOwner(); - if (me.isVisible()) { - return me; - } - if (ownerCt.isGroupHeader) { - ownerCt.lastHiddenHeader = null; - } - if (me.rendered) { - // Size all other columns to accommodate re-shown column. - if (rootHeaderCt.forceFit) { - rootHeaderCt.applyForceFit(me); - } - } - Ext.suspendLayouts(); - // If a sub header, ensure that the group header is visible - if (me.isSubHeader && ownerCt.hidden) { - ownerCt.show(false, true); - } - me.callParent(arguments); - if (me.isGroupHeader) { - me.maybeShowNestedGroupHeader(); - } - // Notify owning HeaderContainer. Will trigger a layout and a view refresh. - ownerCt = me.getRootHeaderCt(); - if (ownerCt) { - ownerCt.onHeaderShow(me); - } - Ext.resumeLayouts(true); - return me; - }, - /** - * @private - * Decides whether the column needs updating - * @return {Number} 0 = Doesn't need update. - * 1 = Column needs update, and renderer has > 1 argument; We need to render a whole new HTML item. - * 2 = Column needs update, but renderer has 1 argument or column uses an updater. - */ - shouldUpdateCell: function(record, changedFieldNames) { - // If the column has a renderer which peeks and pokes at other data, - // return 1 which means that a whole new TableView item must be rendered. - // - // Note that widget columns shouldn't ever be updated. - if (!this.preventUpdate) { - if (this.hasCustomRenderer) { - return 1; - } - // If there is a changed field list, and it's NOT a custom column renderer - // (meaning it doesn't peek at other data, but just uses the raw field value), - // we only have to update it if the column's field is among those changes. - if (changedFieldNames) { - var len = changedFieldNames.length, - i, field; - for (i = 0; i < len; ++i) { - field = changedFieldNames[i]; - if (field === this.dataIndex || field === record.idProperty) { - return 2; - } - } - } else { - return 2; - } - } - }, - getCellWidth: function() { - var me = this, - result; - if (me.rendered && me.componentLayout && me.componentLayout.lastComponentSize) { - // headers always have either a width or a flex - // because HeaderContainer sets a defaults width - // therefore we can ignore the natural width - // we use the componentLayout's tracked width so that - // we can calculate the desired width when rendered - // but not visible because its being obscured by a layout - result = me.componentLayout.lastComponentSize.width; - } else if (me.width) { - result = me.width; - } - // This is a group header. - // Use getTableWidth and remember that getTableWidth adjusts for column lines and box model - else if (!me.isColumn) { - result = me.getTableWidth(); - } - return result; - }, - getCellId: function() { - return Ext.baseCSSPrefix + 'grid-cell-' + this.getItemId(); - }, - getCellSelector: function() { - var view = this.getView(); - // We must explicitly access the view's cell selector as well as this column's own ID class because - // elements are given this column's ID class. - // If we are still atached to a view. If not, the identifying class will do. - return (view ? view.getCellSelector() : '') + '.' + this.getCellId(); - }, - getCellInnerSelector: function() { - return this.getCellSelector() + ' .' + Ext.baseCSSPrefix + 'grid-cell-inner'; - }, - isAtStartEdge: function(e) { - var offset = e.getXY()[0] - this.getX(); - // To the left of the first column, not over - if (offset < 0 && this.getIndex() === 0) { - return false; - } - return (offset < this.getHandleWidth(e)); - }, - isAtEndEdge: function(e, margin) { - return (this.getX() + this.getWidth() - e.getXY()[0] <= (margin || this.getHandleWidth(e))); - }, - getHandleWidth: function(e) { - return e.pointerType === 'touch' ? 10 : 4; - }, - setMenuActive: function(menu) { - // Called when the column menu is activated/deactivated. - // Change the UI to indicate active/inactive menu - this.activeMenu = menu; - this.titleEl[menu ? 'addCls' : 'removeCls'](this.headerOpenCls); - }, - deprecated: { - 5: { - methods: { - bindRenderer: function(renderer) { - // This method restores the pre-5 meaning of "renderer" as a string: - // a method in Ext.util.Format. But at least we don't send all of - // the renderer arguments at the poor thing! - return function(value) { - return Ext.util.Format[renderer](value); - }; - } - } - } - } -}); -// intentionally omit getEditor and setEditor definitions bc we applyIf into columns -// when the editing plugin is injected -/** - * @method getEditor - * Retrieves the editing field for editing associated with this header. If the - * field has not been instantiated it will be created. - * - * **Note:** These methods only have an implementation if an Editing plugin has been - * enabled on the grid ({@link Ext.grid.plugin.CellEditing cellediting} / - * {@link Ext.grid.plugin.RowEditing rowediting}). - * @param {Object} [record] The {@link Ext.data.Model Model} instance being edited. - * @param {Object/String} [defaultField] An xtype or config object for a - * {@link Ext.form.field.Field Field} to be created as the default editor if it does - * not already exist - * @return {Ext.form.field.Field/Boolean} The editor field associated with - * this column. Returns false if there is no field associated with the - * {@link Ext.grid.column.Column Column}. - */ -/** - * @method setEditor - * Sets the form field to be used for editing. Note: This method only has an implementation if an Editing plugin has - * been enabled on the grid. - * @param {Object} field An object representing a field to be created. If no xtype is specified a 'textfield' is - * assumed. - */ - -/** - * A Grid header type which renders an icon, or a series of icons in a grid cell, and offers a scoped click - * handler for each icon. - * - * @example - * // Init the singleton. Any tag-based quick tips will start working. - * Ext.tip.QuickTipManager.init(); - * - * Ext.create('Ext.data.Store', { - * storeId:'employeeStore', - * fields:['firstname', 'lastname', 'seniority', 'dep', 'hired'], - * data:[ - * {firstname:"Michael", lastname:"Scott"}, - * {firstname:"Dwight", lastname:"Schrute"}, - * {firstname:"Jim", lastname:"Halpert"}, - * {firstname:"Kevin", lastname:"Malone"}, - * {firstname:"Angela", lastname:"Martin"} - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Action Column Demo', - * store: Ext.data.StoreManager.lookup('employeeStore'), - * columns: [ - * {text: 'First Name', dataIndex:'firstname'}, - * {text: 'Last Name', dataIndex:'lastname'}, - * { - * xtype:'actioncolumn', - * width:50, - * items: [{ - * icon: 'extjs-build/examples/shared/icons/fam/cog_edit.png', // Use a URL in the icon config - * tooltip: 'Edit', - * handler: function(grid, rowIndex, colIndex) { - * var rec = grid.getStore().getAt(rowIndex); - * alert("Edit " + rec.get('firstname')); - * } - * },{ - * icon: 'extjs-build/examples/restful/images/delete.png', - * tooltip: 'Delete', - * handler: function(grid, rowIndex, colIndex) { - * var rec = grid.getStore().getAt(rowIndex); - * alert("Terminate " + rec.get('firstname')); - * } - * }] - * } - * ], - * width: 250, - * renderTo: Ext.getBody() - * }); - * - * The action column can be at any index in the columns array, and a grid can have any number of - * action columns. - */ -Ext.define('Ext.grid.column.Action', { - extend: 'Ext.grid.column.Column', - alias: [ - 'widget.actioncolumn' - ], - alternateClassName: 'Ext.grid.ActionColumn', - /** - * @cfg {String} [icon=Ext#BLANK_IMAGE_URL] - * @inheritdoc Ext.panel.Header#icon - */ - /** - * @cfg {String} iconCls - * @inheritdoc Ext.panel.Header#cfg-iconCls - * @localdoc **Note:** To determine the class dynamically, configure the Column with - * a `{@link #getClass}` function. - */ - /** - * @cfg {Function/String} handler - * A function called when the icon is clicked. - * @cfg {Ext.view.Table} handler.view The owning TableView. - * @cfg {Number} handler.rowIndex The row index clicked on. - * @cfg {Number} handler.colIndex The column index clicked on. - * @cfg {Object} handler.item The clicked item (or this Column if multiple {@link #cfg-items} were not configured). - * @cfg {Event} handler.e The click event. - * @cfg {Ext.data.Model} handler.record The Record underlying the clicked row. - * @cfg {HTMLElement} handler.row The table row clicked upon. - * @declarativeHandler - */ - /** - * @cfg {Object} scope - * The scope (`this` reference) in which the `{@link #handler}`, - * `{@link #getClass}`, `{@link #cfg-isDisabled}` and `{@link #getTip}` functions - * are executed. - * Defaults to this Column. - */ - /** - * @cfg {String} tooltip - * A tooltip message to be displayed on hover. {@link Ext.tip.QuickTipManager#init Ext.tip.QuickTipManager} must - * have been initialized. - * - * The tooltip may also be determined on a row by row basis by configuring a {@link #getTip} method. - */ - /** - * @cfg {Boolean} disabled - * If true, the action will not respond to click events, and will be displayed semi-opaque. - * - * This Column may also be disabled on a row by row basis by configuring a {@link #cfg-isDisabled} method. - */ - /** - * @cfg {Boolean} [stopSelection=true] - * Prevent grid selection upon click. - * Beware that if you allow for the selection to happen then the selection model will steal focus from - * any possible floating window (like a message box) raised in the handler. This will prevent closing the - * window when pressing the Escape button since it will no longer contain a focused component. - */ - stopSelection: true, - /** - * @cfg {Function} getClass - * A function which returns the CSS class to apply to the icon image. - * - * For information on using the icons provided in the SDK see {@link #iconCls}. - * @cfg {Object} getClass.v The value of the column's configured field (if any). - * @cfg {Object} getClass.metadata An object in which you may set the following attributes: - * @cfg {String} getClass.metadata.css A CSS class name to add to the cell's TD element. - * @cfg {String} getClass.metadata.attr An HTML attribute definition string to apply to the data container - * element *within* the table cell (e.g. 'style="color:red;"'). - * @cfg {Ext.data.Model} getClass.r The Record providing the data. - * @cfg {Number} getClass.rowIndex The row index. - * @cfg {Number} getClass.colIndex The column index. - * @cfg {Ext.data.Store} getClass.store The Store which is providing the data Model. - */ - /** - * @cfg {Function} isDisabled A function which determines whether the action item for any row is disabled and returns `true` or `false`. - * @cfg {Ext.view.Table} isDisabled.view The owning TableView. - * @cfg {Number} isDisabled.rowIndex The row index. - * @cfg {Number} isDisabled.colIndex The column index. - * @cfg {Object} isDisabled.item The clicked item (or this Column if multiple {@link #cfg-items} were not configured). - * @cfg {Ext.data.Model} isDisabled.record The Record underlying the row. - */ - /** - * @cfg {Function} getTip A function which returns the tooltip string for any row. - * - * *Note*: Outside of an Ext.application() use of this config requires - * {@link Ext.tip.QuickTipManager#init} to be called. - * - * Ext.tip.QuickTipManager.init(); - * - * Ext.create('Ext.data.Store', { - * storeId: 'employeeStore', - * fields: ['firstname', 'grade'], - * data: [{ - * firstname: "Michael", - * grade: 50 - * }, { - * firstname: "Dwight", - * grade: 100 - * }] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Action Column Demo', - * store: Ext.data.StoreManager.lookup('employeeStore'), - * columns: [{ - * text: 'First Name', - * dataIndex: 'firstname' - * }, { - * text: 'Last Name', - * dataIndex: 'grade' - * }, { - * xtype: 'actioncolumn', - * width: 50, - * icon: 'sample/icons/action-icons.png', - * getTip: function(value, metadata, record, row, col, store) { - * var avg = store.average('grade'), - * grade = record.get('grade'); - * - * if (grade < avg) { - * metadata.tdCls = "below-average"; - * } - * - * return grade > 70 ? 'Pass' : 'Fail'; - * }, - * handler: function(grid, rowIndex, colIndex) { - * var rec = grid.getStore().getAt(rowIndex); - * alert("Edit " + rec.get('firstname')); - * } - * }], - * width: 250, - * renderTo: document.body - * }); - * - * @param {Object} value The value of the column's configured field (if any). - * @param {Object} metadata An object in which you may set the following attributes: - * @param {String} metadata.tdCls A CSS class name to add to the cell's TD element. - * - * metadata.tdCls = "custom-cell-cls"; - * - * @param {String} metadata.tdAttr An HTML attribute definition string to apply to - * the data container element _within_ the table cell. - * - * metadata.tdCls = tdAttr = "*"; - * // * see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes - * // be aware that setting cell attributes may override the cell layout - * // provided by the framework - * - * @param {String} metadata.tdStyle An inline style for the table cell - * - * metadata.tdStyle = "background-color:red;"; - * - * @param {Ext.data.Model} record The Record providing the data. - * @param {Number} rowIndex The row index. - * @param {Number} colIndex The column index. - * @param {Ext.data.Store} store The Store which is providing the data Model. - * @return {String} tip The tip text - */ - /** - * @cfg {Object[]} items - * An Array which may contain multiple icon definitions, each element of which may contain: - * - * @cfg {String} items.icon The url of an image to display as the clickable element in the column. - * - * @cfg {String} items.iconCls A CSS class to apply to the icon element. To - * determine the class dynamically, configure the item with a `getClass` function. - * - * For information on using the icons provided in the SDK see {@link #iconCls}. - * - * @cfg {Function} items.getClass A function which returns the CSS class to apply to the icon image. - * @cfg {Object} items.getClass.v The value of the column's configured field (if any). - * @cfg {Object} items.getClass.metadata An object in which you may set the following attributes: - * @cfg {String} items.getClass.metadata.css A CSS class name to add to the cell's TD element. - * @cfg {String} items.getClass.metadata.attr An HTML attribute definition string to apply to the data - * container element _within_ the table cell (e.g. 'style="color:red;"'). - * @cfg {Ext.data.Model} items.getClass.r The Record providing the data. - * @cfg {Number} items.getClass.rowIndex The row index. - * @cfg {Number} items.getClass.colIndex The column index. - * @cfg {Ext.data.Store} items.getClass.store The Store which is providing the data Model. - * - * @cfg {Function} items.handler A function called when the icon is clicked. - * @cfg {Ext.view.Table} items.handler.view The owning TableView. - * @cfg {Number} items.handler.rowIndex The row index clicked on. - * @cfg {Number} items.handler.colIndex The column index clicked on. - * @cfg {Object} items.handler.item The clicked item (or this Column if multiple {@link #cfg-items} were not configured). - * @cfg {Event} items.handler.e The click event. - * @cfg {Ext.data.Model} items.handler.record The Record underlying the clicked row. - * @cfg {HTMLElement} items.row The table row clicked upon. - * - * @cfg {Function} items.isDisabled A function which determines whether the action item for any row is disabled and returns `true` or `false`. - * @cfg {Ext.view.Table} items.isDisabled.view The owning TableView. - * @cfg {Number} items.isDisabled.rowIndex The row index. - * @cfg {Number} items.isDisabled.colIndex The column index. - * @cfg {Object} items.isDisabled.item The clicked item (or this Column if multiple {@link #cfg-items} were not configured). - * @cfg {Ext.data.Model} items.isDisabled.record The Record underlying the row. - * - * @cfg {Function} items.getTip A function which returns the tooltip string for any row. - * @cfg {Object} items.getTip.v The value of the column's configured field (if any). - * @cfg {Object} items.getTip.metadata An object in which you may set the following attributes: - * @cfg {String} items.getTip.metadata.css A CSS class name to add to the cell's TD element. - * @cfg {String} items.getTip.metadata.attr An HTML attribute definition string to apply to the data - * container element _within_ the table cell (e.g. 'style="color:red;"'). - * @cfg {Ext.data.Model} items.getTip.r The Record providing the data. - * @cfg {Number} items.getTip.rowIndex The row index. - * @cfg {Number} items.getTip.colIndex The column index. - * @cfg {Ext.data.Store} items.getTip.store The Store which is providing the data Model. - * - * @cfg {Object} items.scope The scope (`this` reference) in which the `handler`, `getClass`, `isDisabled` and `getTip` functions - * are executed. Fallback defaults are this Column's configured scope, then this Column. - * - * @cfg {String} items.tooltip A tooltip message to be displayed on hover. - * {@link Ext.tip.QuickTipManager#init Ext.tip.QuickTipManager} must have been initialized. - * - * The tooltip may also be determined on a row by row basis by configuring a `getTip` method. - * - * @cfg {Boolean} items.disabled If true, the action will not respond to click events, and will be displayed semi-opaque. - * - * This item may also be disabled on a row by row basis by configuring an `isDisabled` method. - */ - /** - * @property {Array} items - * An array of action items copied from the configured {@link #cfg-items items} configuration. Each will have - * an `enable` and `disable` method added which will enable and disable the associated action, and - * update the displayed icon accordingly. - */ - actionIdRe: new RegExp(Ext.baseCSSPrefix + 'action-col-(\\d+)'), - /** - * @cfg {String} altText - * The alt text to use for the image element. - */ - altText: '', - /** - * @cfg {String} [menuText=Actions] - * Text to display in this column's menu item if no {@link #text} was specified as a header. - */ - menuText: 'Actions', - ignoreExport: true, - sortable: false, - innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-action-col', - actionIconCls: Ext.baseCSSPrefix + 'action-col-icon', - constructor: function(config) { - var me = this, - cfg = Ext.apply({}, config), - // Items may be defined on the prototype - items = cfg.items || me.items || [ - me - ], - hasGetClass, i, len; - me.origRenderer = cfg.renderer || me.renderer; - me.origScope = cfg.scope || me.scope; - me.renderer = me.scope = cfg.renderer = cfg.scope = null; - // This is a Container. Delete the items config to be reinstated after construction. - cfg.items = null; - me.callParent([ - cfg - ]); - // Items is an array property of ActionColumns - me.items = items; - for (i = 0 , len = items.length; i < len; ++i) { - if (items[i].getClass) { - hasGetClass = true; - break; - } - } - // Also need to check for getClass, since it changes how the cell renders - if (me.origRenderer || hasGetClass) { - me.hasCustomRenderer = true; - } - }, - initComponent: function() { - var me = this; - me.callParent(); - if (me.sortable && !me.dataIndex) { - me.sortable = false; - } - }, - // Renderer closure iterates through items creating an element for each and tagging with an identifying - // class name x-action-col-{n} - defaultRenderer: function(v, cellValues, record, rowIdx, colIdx, store, view) { - var me = this, - scope = me.origScope || me, - items = me.items, - len = items.length, - i, item, ret, disabled, tooltip, altText, icon; - // Allow a configured renderer to create initial value (And set the other values in the "metadata" argument!) - // Assign a new variable here, since if we modify "v" it will also modify the arguments collection, meaning - // we will pass an incorrect value to getClass/getTip - ret = Ext.isFunction(me.origRenderer) ? me.origRenderer.apply(scope, arguments) || '' : ''; - cellValues.tdCls += ' ' + Ext.baseCSSPrefix + 'action-col-cell'; - for (i = 0; i < len; i++) { - item = items[i]; - icon = item.icon; - disabled = item.disabled || (item.isDisabled ? item.isDisabled.call(item.scope || scope, view, rowIdx, colIdx, item, record) : false); - tooltip = disabled ? null : (item.tooltip || (item.getTip ? item.getTip.apply(item.scope || scope, arguments) : null)); - altText = item.getAltText ? item.getAltText.apply(item.scope || scope, arguments) : item.altText || me.altText; - // Only process the item action setup once. - if (!item.hasActionConfiguration) { - // Apply our documented default to all items - item.stopSelection = me.stopSelection; - item.disable = Ext.Function.bind(me.disableAction, me, [ - i - ], 0); - item.enable = Ext.Function.bind(me.enableAction, me, [ - i - ], 0); - item.hasActionConfiguration = true; - } - ret += '<' + (icon ? 'img' : 'div') + ' tabIndex="0" role="button"' + (icon ? (' alt="' + altText + '" src="' + item.icon + '"') : '') + ' class="' + me.actionIconCls + ' ' + Ext.baseCSSPrefix + 'action-col-' + String(i) + ' ' + (disabled ? me.disabledCls + ' ' : ' ') + (Ext.isFunction(item.getClass) ? item.getClass.apply(item.scope || scope, arguments) : (item.iconCls || me.iconCls || '')) + '"' + (tooltip ? ' data-qtip="' + tooltip + '"' : '') + (icon ? '/>' : '>'); - } - return ret; - }, - updater: function(cell, value, record, view, dataSource) { - var cellValues = {}; - Ext.fly(cell).addCls(cellValues.tdCls).down(this.getView().innerSelector, true).innerHTML = this.defaultRenderer(value, cellValues, record, null, null, dataSource, view); - }, - /** - * Enables this ActionColumn's action at the specified index. - * @param {Number/Ext.grid.column.Action} index - * @param {Boolean} [silent=false] - */ - enableAction: function(index, silent) { - var me = this; - if (!index) { - index = 0; - } else if (!Ext.isNumber(index)) { - index = Ext.Array.indexOf(me.items, index); - } - me.items[index].disabled = false; - me.up('tablepanel').el.select('.' + Ext.baseCSSPrefix + 'action-col-' + index).removeCls(me.disabledCls); - if (!silent) { - me.fireEvent('enable', me); - } - }, - /** - * Disables this ActionColumn's action at the specified index. - * @param {Number/Ext.grid.column.Action} index - * @param {Boolean} [silent=false] - */ - disableAction: function(index, silent) { - var me = this; - if (!index) { - index = 0; - } else if (!Ext.isNumber(index)) { - index = Ext.Array.indexOf(me.items, index); - } - me.items[index].disabled = true; - me.up('tablepanel').el.select('.' + Ext.baseCSSPrefix + 'action-col-' + index).addCls(me.disabledCls); - if (!silent) { - me.fireEvent('disable', me); - } - }, - beforeDestroy: function() { - // Don't delete the items, if we're subclassed with items then we'll be - // left with an items array. - this.renderer = this.items = null; - return this.callParent(arguments); - }, - /** - * @private - * Process and re-fire events routed from the Ext.panel.Table's processEvent method. - * Also fires any configured click handlers. By default, cancels the mousedown event to prevent selection. - * Returns the event handler's status to allow canceling of GridView's bubbling process. - */ - processEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) { - var me = this, - target = e.getTarget(), - key = type === 'keydown' && e.getKey(), - match, item, disabled, - cellFly = Ext.fly(cell); - // Flag event to tell SelectionModel not to process it. - e.stopSelection = !key && me.stopSelection; - // If the target was not within a cell (ie it's a keydown event from the View), then - // IF there's only one action icon, action it. If there is more than one, the user must - // invoke actionable mode to navigate into the cell. - if (key && (target === cell || !cellFly.contains(target))) { - target = cellFly.query('.' + me.actionIconCls, true); - if (target.length === 1) { - target = target[0]; - } else { - return; - } - } - // NOTE: The statement below tests the truthiness of an assignment. - if (target && (match = target.className.match(me.actionIdRe))) { - item = me.items[parseInt(match[1], 10)]; - disabled = item.disabled || (item.isDisabled ? item.isDisabled.call(item.scope || me.origScope || me, view, recordIndex, cellIndex, item, record) : false); - if (item && !disabled) { - // Do not allow focus to follow from this mousedown unless the grid is already in actionable mode - if (type === 'mousedown' && !me.getView().actionableMode) { - e.preventDefault(); - } else if (type === 'click' || (key === e.ENTER || key === e.SPACE)) { - Ext.callback(item.handler || me.handler, item.scope || me.origScope, [ - view, - recordIndex, - cellIndex, - item, - e, - record, - row - ], undefined, me); - // If the handler moved focus outside of the view, do not allow this event to propagate - // to cause any navigation. - if (!view.el.contains(Ext.Element.getActiveElement())) { - return false; - } - } - } - } - return me.callParent(arguments); - }, - cascade: function(fn, scope) { - fn.call(scope || this, this); - }, - // Private override because this cannot function as a Container, and it has an items property which is an Array, NOT a MixedCollection. - getRefItems: function() { - return []; - }, - privates: { - getFocusables: function() { - // Override is here to prevent the default behaviour which tries to access - // this.items.items, which will be null. - return []; - }, - // Overriden method to always return a bitwise value that will result in a call to this column's updater. - shouldUpdateCell: function() { - return 2; - } - } -}); - -/** - * A Column definition class which renders boolean data fields. See the {@link Ext.grid.column.Column#xtype xtype} - * config option of {@link Ext.grid.column.Column} for more details. - * - * @example - * var store = Ext.create('Ext.data.Store', { - * fields: [ - * {name: 'framework', type: 'string'}, - * {name: 'rocks', type: 'boolean'} - * ], - * data: [ - * { framework: 'Ext JS 5', rocks: true }, - * { framework: 'Ext GWT', rocks: true }, - * { framework: 'Other Guys', rocks: false } - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Boolean Column Demo', - * store: store, - * columns: [ - * { text: 'Framework', dataIndex: 'framework', flex: 1 }, - * { - * xtype: 'booleancolumn', - * text: 'Rocks', - * trueText: 'Yes', - * falseText: 'No', - * dataIndex: 'rocks' - * } - * ], - * height: 200, - * width: 400, - * renderTo: Ext.getBody() - * }); - */ -Ext.define('Ext.grid.column.Boolean', { - extend: 'Ext.grid.column.Column', - alias: [ - 'widget.booleancolumn' - ], - alternateClassName: 'Ext.grid.BooleanColumn', - // - /** - * @cfg {String} trueText - * The string returned by the renderer when the column value is not falsey. - */ - trueText: 'true', - // - // - /** - * @cfg {String} falseText - * The string returned by the renderer when the column value is falsey (but not undefined). - */ - falseText: 'false', - // - /** - * @cfg {String} undefinedText - * The string returned by the renderer when the column value is undefined. - */ - undefinedText: ' ', - defaultFilterType: 'boolean', - /** - * @cfg {Object} renderer - * @hide - */ - /** - * @cfg {Object} scope - * @hide - */ - /** - * @cfg {Boolean} producesHTML - * @inheritdoc - */ - producesHTML: false, - defaultRenderer: function(value) { - if (value === undefined) { - return this.undefinedText; - } - if (!value || value === 'false') { - return this.falseText; - } - return this.trueText; - }, - updater: function(cell, value) { - Ext.fly(cell).down(this.getView().innerSelector, true).innerHTML = Ext.grid.column.Boolean.prototype.defaultRenderer.call(this, value); - } -}); - -/** - * A Column subclass which renders a checkbox in each column cell which toggles the truthiness of the associated data field on click. - * - * Example usage: - * - * @example - * var store = Ext.create('Ext.data.Store', { - * fields: ['name', 'email', 'phone', 'active'], - * data: [ - * { name: 'Lisa', email: 'lisa@simpsons.com', phone: '555-111-1224', active: true }, - * { name: 'Bart', email: 'bart@simpsons.com', phone: '555-222-1234', active: true }, - * { name: 'Homer', email: 'homer@simpsons.com', phone: '555-222-1244', active: false }, - * { name: 'Marge', email: 'marge@simpsons.com', phone: '555-222-1254', active: true } - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Simpsons', - * height: 200, - * width: 400, - * renderTo: Ext.getBody(), - * store: store, - * columns: [ - * { text: 'Name', dataIndex: 'name' }, - * { text: 'Email', dataIndex: 'email', flex: 1 }, - * { text: 'Phone', dataIndex: 'phone' }, - * { xtype: 'checkcolumn', text: 'Active', dataIndex: 'active' } - * ] - * }); - * - * The check column can be at any index in the columns array. - */ -Ext.define('Ext.grid.column.Check', { - extend: 'Ext.grid.column.Column', - alternateClassName: [ - 'Ext.ux.CheckColumn', - 'Ext.grid.column.CheckColumn' - ], - alias: 'widget.checkcolumn', - /** - * @cfg - * @hide - * Overridden from base class. Must center to line up with editor. - */ - align: 'center', - ignoreExport: true, - /** - * @cfg {Boolean} [stopSelection=true] - * Prevent grid selection upon mousedown. - */ - stopSelection: true, - tdCls: Ext.baseCSSPrefix + 'grid-cell-checkcolumn', - innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-checkcolumn', - clickTargetName: 'el', - defaultFilterType: 'boolean', - /** - * @event beforecheckchange - * Fires when before checked state of a row changes. - * The change may be vetoed by returning `false` from a listener. - * @param {Ext.ux.CheckColumn} this CheckColumn - * @param {Number} rowIndex The row index - * @param {Boolean} checked True if the box is to be checked - */ - /** - * @event checkchange - * Fires when the checked state of a row changes - * @param {Ext.ux.CheckColumn} this CheckColumn - * @param {Number} rowIndex The row index - * @param {Boolean} checked True if the box is now checked - */ - constructor: function() { - this.scope = this; - this.callParent(arguments); - }, - /** - * @private - * Process and refire events routed from the GridView's processEvent method. - */ - processEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) { - var me = this, - key = type === 'keydown' && e.getKey(), - mousedown = type === 'mousedown', - disabled = me.disabled, - ret, checked; - // Flag event to tell SelectionModel not to process it. - e.stopSelection = !key && me.stopSelection; - if (!disabled && (mousedown || (key === e.ENTER || key === e.SPACE))) { - checked = !me.isRecordChecked(record); - // Allow apps to hook beforecheckchange - if (me.fireEvent('beforecheckchange', me, recordIndex, checked) !== false) { - me.setRecordCheck(record, checked, cell, row, e); - me.fireEvent('checkchange', me, recordIndex, checked); - // Do not allow focus to follow from this mousedown unless the grid is already in actionable mode - if (mousedown && !me.getView().actionableMode) { - e.preventDefault(); - } - } - } else { - ret = me.callParent(arguments); - } - return ret; - }, - /** - * Enables this CheckColumn. - */ - onEnable: function() { - this.callParent(arguments); - this._setDisabled(false); - }, - /** - * Disables this CheckColumn. - */ - onDisable: function() { - this._setDisabled(true); - }, - // Don't want to conflict with the Component method - _setDisabled: function(disabled) { - var me = this, - cls = me.disabledCls, - items; - items = me.up('tablepanel').el.select(me.getCellSelector()); - if (disabled) { - items.addCls(cls); - } else { - items.removeCls(cls); - } - }, - // Note: class names are not placed on the prototype bc renderer scope - // is not in the header. - defaultRenderer: function(value, cellValues) { - var cssPrefix = Ext.baseCSSPrefix, - cls = cssPrefix + 'grid-checkcolumn'; - if (this.disabled) { - cellValues.tdCls += ' ' + this.disabledCls; - } - if (value) { - cls += ' ' + cssPrefix + 'grid-checkcolumn-checked'; - } - return '
    '; - }, - isRecordChecked: function(record) { - var prop = this.property; - if (prop) { - return record[prop]; - } - return record.get(this.dataIndex); - }, - setRecordCheck: function(record, checked, cell, row, e) { - var me = this, - prop = me.property; - if (prop) { - record[prop] = checked; - me.updater(cell, checked); - } else { - record.set(me.dataIndex, checked); - } - }, - updater: function(cell, value) { - cell = Ext.fly(cell); - cell[this.disabled ? 'addCls' : 'removeCls'](this.disabledCls); - Ext.fly(cell.down(this.getView().innerSelector, true).firstChild)[value ? 'addCls' : 'removeCls'](Ext.baseCSSPrefix + 'grid-checkcolumn-checked'); - } -}); - -/** - * A Column definition class which renders a passed date according to the default locale, or a configured - * {@link #format}. - * - * @example - * Ext.create('Ext.data.Store', { - * storeId:'sampleStore', - * fields:[ - * { name: 'symbol', type: 'string' }, - * { name: 'date', type: 'date' }, - * { name: 'change', type: 'number' }, - * { name: 'volume', type: 'number' }, - * { name: 'topday', type: 'date' } - * ], - * data:[ - * { symbol: "msft", date: '2011/04/22', change: 2.43, volume: 61606325, topday: '04/01/2010' }, - * { symbol: "goog", date: '2011/04/22', change: 0.81, volume: 3053782, topday: '04/11/2010' }, - * { symbol: "apple", date: '2011/04/22', change: 1.35, volume: 24484858, topday: '04/28/2010' }, - * { symbol: "sencha", date: '2011/04/22', change: 8.85, volume: 5556351, topday: '04/22/2010' } - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Date Column Demo', - * store: Ext.data.StoreManager.lookup('sampleStore'), - * columns: [ - * { text: 'Symbol', dataIndex: 'symbol', flex: 1 }, - * { text: 'Date', dataIndex: 'date', xtype: 'datecolumn', format:'Y-m-d' }, - * { text: 'Change', dataIndex: 'change', xtype: 'numbercolumn', format:'0.00' }, - * { text: 'Volume', dataIndex: 'volume', xtype: 'numbercolumn', format:'0,000' }, - * { text: 'Top Day', dataIndex: 'topday', xtype: 'datecolumn', format:'l' } - * ], - * height: 200, - * width: 450, - * renderTo: Ext.getBody() - * }); - */ -Ext.define('Ext.grid.column.Date', { - extend: 'Ext.grid.column.Column', - alias: [ - 'widget.datecolumn' - ], - requires: [ - 'Ext.Date' - ], - alternateClassName: 'Ext.grid.DateColumn', - isDateColumn: true, - defaultFilterType: 'date', - /** - * @cfg {String} format - * A formatting string as used by {@link Ext.Date#format} to format a Date for this Column. - * - * Defaults to the default date from {@link Ext.Date#defaultFormat} which itself my be overridden - * in a locale file. - */ - /** - * @cfg {Object} renderer - * @hide - */ - /** - * @cfg {Object} scope - * @hide - */ - /** - * @cfg {Boolean} producesHTML - * @inheritdoc - */ - producesHTML: false, - initComponent: function() { - if (!this.format) { - this.format = Ext.Date.defaultFormat; - } - this.callParent(arguments); - }, - defaultRenderer: function(value) { - return Ext.util.Format.date(value, this.format); - }, - updater: function(cell, value) { - Ext.fly(cell).down(this.getView().innerSelector, true).innerHTML = Ext.grid.column.Date.prototype.defaultRenderer.call(this, value); - } -}); - -/** - * A Column definition class which renders a numeric data field according to a {@link #format} string. - * - * @example - * Ext.create('Ext.data.Store', { - * storeId:'sampleStore', - * fields:[ - * { name: 'symbol', type: 'string' }, - * { name: 'price', type: 'number' }, - * { name: 'change', type: 'number' }, - * { name: 'volume', type: 'number' } - * ], - * data:[ - * { symbol: "msft", price: 25.76, change: 2.43, volume: 61606325 }, - * { symbol: "goog", price: 525.73, change: 0.81, volume: 3053782 }, - * { symbol: "apple", price: 342.41, change: 1.35, volume: 24484858 }, - * { symbol: "sencha", price: 142.08, change: 8.85, volume: 5556351 } - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Number Column Demo', - * store: Ext.data.StoreManager.lookup('sampleStore'), - * columns: [ - * { text: 'Symbol', dataIndex: 'symbol', flex: 1 }, - * { text: 'Current Price', dataIndex: 'price', renderer: Ext.util.Format.usMoney }, - * { text: 'Change', dataIndex: 'change', xtype: 'numbercolumn', format:'0.00' }, - * { text: 'Volume', dataIndex: 'volume', xtype: 'numbercolumn', format:'0,000' } - * ], - * height: 200, - * width: 400, - * renderTo: Ext.getBody() - * }); - */ -Ext.define('Ext.grid.column.Number', { - extend: 'Ext.grid.column.Column', - alias: [ - 'widget.numbercolumn' - ], - requires: [ - 'Ext.util.Format' - ], - alternateClassName: 'Ext.grid.NumberColumn', - defaultFilterType: 'number', - // - /** - * @cfg {String} format - * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column. - */ - format: '0,000.00', - // - /** - * @cfg {Object} renderer - * @hide - */ - /** - * @cfg {Object} scope - * @hide - */ - /** - * @cfg {Boolean} producesHTML - * @inheritdoc - */ - producesHTML: false, - defaultRenderer: function(value) { - return Ext.util.Format.number(value, this.format); - }, - updater: function(cell, value) { - Ext.fly(cell).down(this.getView().innerSelector, true).innerHTML = Ext.grid.column.Number.prototype.defaultRenderer.call(this, value); - } -}); - -/** - * A special type of Grid {@link Ext.grid.column.Column} that provides automatic - * row numbering. - * - * Usage: - * - * columns: [ - * {xtype: 'rownumberer'}, - * {text: "Company", flex: 1, sortable: true, dataIndex: 'company'}, - * {text: "Price", width: 120, sortable: true, renderer: Ext.util.Format.usMoney, dataIndex: 'price'}, - * {text: "Change", width: 120, sortable: true, dataIndex: 'change'}, - * {text: "% Change", width: 120, sortable: true, dataIndex: 'pctChange'}, - * {text: "Last Updated", width: 120, sortable: true, renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'} - * ] - * - */ -Ext.define('Ext.grid.column.RowNumberer', { - extend: 'Ext.grid.column.Column', - alternateClassName: 'Ext.grid.RowNumberer', - alias: 'widget.rownumberer', - /** - * @property {Boolean} isRowNumberer - * `true` in this class to identify an object as an instantiated RowNumberer, or subclass thereof. - */ - isRowNumberer: true, - /** - * @cfg {String} text - * Any valid text or HTML fragment to display in the header cell for the row number column. - */ - text: " ", - /** - * @cfg {Number} width - * The default width in pixels of the row number column. - */ - width: 23, - /** - * @cfg {Boolean} sortable - * @hide - */ - sortable: false, - /** - * @cfg {Boolean} [draggable=false] - * False to disable drag-drop reordering of this column. - */ - draggable: false, - // Flag to Lockable to move instances of this column to the locked side. - autoLock: true, - // May not be moved from its preferred locked side when grid is enableLocking:true - lockable: false, - align: 'right', - /** - * @cfg {Boolean} producesHTML - * @inheritdoc - */ - producesHTML: false, - ignoreExport: true, - constructor: function(config) { - var me = this; - // Copy the prototype's default width setting into an instance property to provide - // a default width which will not be overridden by Container.applyDefaults use of Ext.applyIf - me.width = me.width; - me.callParent(arguments); - // Override any setting from the HeaderContainer's defaults - me.sortable = false; - me.scope = me; - }, - resizable: false, - hideable: false, - menuDisabled: true, - dataIndex: '', - cls: Ext.baseCSSPrefix + 'row-numberer', - tdCls: Ext.baseCSSPrefix + 'grid-cell-row-numberer ' + Ext.baseCSSPrefix + 'grid-cell-special', - innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-row-numberer', - rowspan: undefined, - defaultRenderer: function(value, metaData, record, rowIdx, colIdx, dataSource, view) { - var rowspan = this.rowspan, - page = dataSource.currentPage, - result = view.store.indexOf(record); - if (metaData && rowspan) { - metaData.tdAttr = 'rowspan="' + rowspan + '"'; - } - if (page > 1) { - result += (page - 1) * dataSource.pageSize; - } - return result + 1; - }, - updater: function(cell, value, record, view, dataSource) { - Ext.fly(cell).down(this.getView().innerSelector, true).innerHTML = this.defaultRenderer(value, null, record, null, null, dataSource, view); - } -}); - -/** - * A Column definition class which renders a value by processing a {@link Ext.data.Model Model}'s - * {@link Ext.data.Model#getData data} using a {@link #tpl configured} - * {@link Ext.XTemplate XTemplate}. - * - * @example - * Ext.create('Ext.data.Store', { - * storeId:'employeeStore', - * fields:['firstname', 'lastname', 'seniority', 'department'], - * groupField: 'department', - * data:[ - * { firstname: "Michael", lastname: "Scott", seniority: 7, department: "Management" }, - * { firstname: "Dwight", lastname: "Schrute", seniority: 2, department: "Sales" }, - * { firstname: "Jim", lastname: "Halpert", seniority: 3, department: "Sales" }, - * { firstname: "Kevin", lastname: "Malone", seniority: 4, department: "Accounting" }, - * { firstname: "Angela", lastname: "Martin", seniority: 5, department: "Accounting" } - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Column Template Demo', - * store: Ext.data.StoreManager.lookup('employeeStore'), - * columns: [ - * { text: 'Full Name', xtype: 'templatecolumn', tpl: '{firstname} {lastname}', flex:1 }, - * { text: 'Department (Yrs)', xtype: 'templatecolumn', tpl: '{department} ({seniority})' } - * ], - * height: 200, - * width: 300, - * renderTo: Ext.getBody() - * }); - */ -Ext.define('Ext.grid.column.Template', { - extend: 'Ext.grid.column.Column', - alias: [ - 'widget.templatecolumn' - ], - requires: [ - 'Ext.XTemplate' - ], - alternateClassName: 'Ext.grid.TemplateColumn', - /** - * @cfg {String/Ext.XTemplate} tpl - * An {@link Ext.XTemplate XTemplate}, or an XTemplate *definition string* to use to process a - * {@link Ext.data.Model Model}'s data object to produce a cell's rendered value. - */ - /** - * @cfg {Object} renderer - * @hide - */ - /** - * @cfg {Object} scope - * @hide - */ - initComponent: function() { - var me = this; - me.tpl = (!Ext.isPrimitive(me.tpl) && me.tpl.compile) ? me.tpl : new Ext.XTemplate(me.tpl); - // Set this here since the template may access any record values, - // so we must always run the update for this column - me.hasCustomRenderer = true; - me.callParent(arguments); - }, - defaultRenderer: function(value, meta, record) { - var data = Ext.apply({}, record.data, record.getAssociatedData()); - return this.tpl.apply(data); - }, - updater: function(cell, value) { - Ext.fly(cell).down(this.getView().innerSelector, true).innerHTML = Ext.grid.column.CheckColumn.prototype.defaultRenderer.call(this, value); - } -}); - -/** - * A widget column is configured with a {@link #widget} config object which specifies an - * {@link Ext.Component#cfg-xtype xtype} to indicate which type of Widget or Component belongs - * in the cells of this column. - * - * When a widget cell is rendered, a {@link Ext.Widget Widget} or {@link Ext.Component Component} of the specified type - * is rendered into that cell. Its {@link Ext.Component#defaultBindProperty defaultBindProperty} is set using this - * column's {@link #dataIndex} field from the associated record. - * - * In the example below we are monitoring the throughput of electricity substations. The capacity being - * used as a proportion of the maximum rated capacity is displayed as a progress bar. As new data arrives and the - * instantaneous usage value is updated, the `capacityUsed` field updates itself, and - * that is used as the {@link #dataIndex} for the `WidgetColumn` which contains the - * progress bar. The progress Bar's - * {@link Ext.ProgressBarWidget#defaultBindProperty defaultBindProperty} (which is - * "value") is set to the calculated `capacityUsed`. - * - * @example - * var grid = new Ext.grid.Panel({ - * title: 'Substation power monitor', - * width: 600, - * columns: [{ - * text: 'Id', - * dataIndex: 'id', - * width: 120 - * }, { - * text: 'Rating', - * dataIndex: 'maxCapacity', - * width: 80 - * }, { - * text: 'Avg.', - * dataIndex: 'avg', - * width: 85, - * formatter: 'number("0.00")' - * }, { - * text: 'Max', - * dataIndex: 'max', - * width: 80 - * }, { - * text: 'Instant', - * dataIndex: 'instant', - * width: 80 - * }, { - * text: '%Capacity', - * width: 150, - * - * // This is our Widget column - * xtype: 'widgetcolumn', - * dataIndex: 'capacityUsed', - * - * // This is the widget definition for each cell. - * // Its "value" setting is taken from the column's dataIndex - * widget: { - * xtype: 'progressbarwidget', - * textTpl: [ - * '{percent:number("0")}% capacity' - * ] - * } - * }], - * renderTo: document.body, - * disableSelection: true, - * store: { - * fields: [{ - * name: 'id', - * type: 'string' - * }, { - * name: 'maxCapacity', - * type: 'int' - * }, { - * name: 'avg', - * type: 'int', - * calculate: function(data) { - * // Make this depend upon the instant field being set which sets the sampleCount and total. - * // Use subscript format to access the other pseudo fields which are set by the instant field's converter - * return data.instant && data['total'] / data['sampleCount']; - * } - * }, { - * name: 'max', - * type: 'int', - * calculate: function(data) { - * // This will be seen to depend on the "instant" field. - * // Use subscript format to access this field's current value to avoid circular dependency error. - * return (data['max'] || 0) < data.instant ? data.instant : data['max']; - * } - * }, { - * name: 'instant', - * type: 'int', - * - * // Upon every update of instantaneous power throughput, - * // update the sample count and total so that the max field can calculate itself - * convert: function(value, rec) { - * rec.data.sampleCount = (rec.data.sampleCount || 0) + 1; - * rec.data.total = (rec.data.total || 0) + value; - * return value; - * }, - * depends: [] - * }, { - * name: 'capacityUsed', - * calculate: function(data) { - * return data.instant / data.maxCapacity; - * } - * }], - * data: [{ - * id: 'Substation A', - * maxCapacity: 1000, - * avg: 770, - * max: 950, - * instant: 685 - * }, { - * id: 'Substation B', - * maxCapacity: 1000, - * avg: 819, - * max: 992, - * instant: 749 - * }, { - * id: 'Substation C', - * maxCapacity: 1000, - * avg: 588, - * max: 936, - * instant: 833 - * }, { - * id: 'Substation D', - * maxCapacity: 1000, - * avg: 639, - * max: 917, - * instant: 825 - * }] - * } - * }); - * - * // Fake data updating... - * // Change one record per second to a random power value - * Ext.interval(function() { - * var recIdx = Ext.Number.randomInt(0, 3), - * newPowerReading = Ext.Number.randomInt(500, 1000); - * - * grid.store.getAt(recIdx).set('instant', newPowerReading); - * }, 1000); - * - * @since 5.0.0 - */ -Ext.define('Ext.grid.column.Widget', { - extend: 'Ext.grid.column.Column', - alias: 'widget.widgetcolumn', - config: { - /** - * @cfg defaultWidgetUI - * A map of xtype to {@link Ext.Component#ui} names to use when using Components in this column. - * - * Currently {@link Ext.Button Button} and all subclasses of {@link Ext.form.field.Text TextField} default - * to using `ui: "default"` when in a WidgetColumn except for in the "classic" theme, when they use ui "grid-cell". - */ - defaultWidgetUI: {} - }, - ignoreExport: true, - /** - * @cfg - * @inheritdoc - */ - sortable: false, - /** - * @cfg {Object} renderer - * @hide - */ - /** - * @cfg {Object} scope - * @hide - */ - /** - * @cfg {Object} widget - * A config object containing an {@link Ext.Component#cfg-xtype xtype}. - * - * This is used to create the widgets or components which are rendered into the cells of this column. - * - * This column's {@link #dataIndex} is used to update the widget/component's {@link Ext.Component#defaultBindProperty defaultBindProperty}. - * - * The widget will be decorated with 2 methods: - * `getWidgetRecord` - Returns the {@link Ext.data.Model record} the widget is associated with. - * `getWidgetColumn` - Returns the {@link Ext.grid.column.Widget column} the widget - * was associated with. - */ - /** - * @cfg {Function/String} onWidgetAttach - * A function that will be called when a widget is attached to a record. This may be useful for - * doing any post-processing. - * - * Ext.create({ - * xtype: 'grid', - * title: 'Student progress report', - * width: 250, - * renderTo: Ext.getBody(), - * disableSelection: true, - * store: { - * fields: ['name', 'isHonorStudent'], - * data: [{ - * name: 'Finn', - * isHonorStudent: true - * }, { - * name: 'Jake', - * isHonorStudent: false - * }] - * }, - * columns: [{ - * text: 'Name', - * dataIndex: 'name', - * flex: 1 - * }, { - * xtype: 'widgetcolumn', - * text: 'Honor Roll', - * dataIndex: 'isHonorStudent', - * width: 150, - * widget: { - * xtype: 'button', - * handler: function() { - * // print certificate handler - * } - * }, - * // called when the widget is initially instantiated - * // on the widget column - * onWidgetAttach: function(col, widget, rec) { - * widget.setText('Print Certificate'); - * widget.setDisabled(!rec.get('isHonorStudent')); - * } - * }] - * }); - * - * @param {Ext.grid.column.Column} column The column. - * @param {Ext.Component/Ext.Widget} widget The {@link #widget} rendered to each cell. - * @param {Ext.data.Model} record The record used with the current widget (cell). - * @declarativeHandler - */ - onWidgetAttach: null, - preventUpdate: true, - /** - * @cfg {Boolean} [stopSelection=true] - * Prevent grid selection upon click on the widget. - */ - stopSelection: true, - initComponent: function() { - var me = this, - widget; - me.callParent(arguments); - widget = me.widget; - if (!widget || widget.isComponent) { - Ext.raise('column.Widget requires a widget configuration.'); - } - me.widget = widget = Ext.apply({}, widget); - // Apply the default UI for the xtype which is going to feature in this column. - if (!widget.ui) { - widget.ui = me.getDefaultWidgetUI()[widget.xtype] || 'default'; - } - me.isFixedSize = Ext.isNumber(widget.width); - }, - processEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) { - var target; - if (this.stopSelection && type === 'click') { - // Grab the target that matches the cell inner selector. If we have a target, then, - // that means we either clicked on the inner part or the widget inside us. If - // target === e.target, then it was on the cell, so it's ok. Otherwise, inside so - // prevent the selection from happening - target = e.getTarget(view.innerSelector); - if (target && target !== e.target) { - e.stopSelection = true; - } - } - }, - beforeRender: function() { - var me = this, - tdCls = me.tdCls, - widget; - me.listenerScopeFn = function(defaultScope) { - if (defaultScope === 'this') { - return this; - } - return me.resolveListenerScope(defaultScope); - }; - me.liveWidgets = {}; - me.cachedStyles = {}; - me.freeWidgetStack = [ - widget = me.getFreeWidget() - ]; - tdCls = tdCls ? tdCls + ' ' : ''; - me.tdCls = tdCls + widget.getTdCls(); - me.setupViewListeners(me.getView()); - me.callParent(); - }, - afterRender: function() { - var view = this.getView(); - this.callParent(); - // View already ready, means we were added later so go and set up our widgets, but if the grid - // is reconfiguring, then the column will be rendered & the view will be ready, so wait until - // the reconfigure forces a refresh - if (view && view.viewReady && !view.ownerGrid.reconfiguring) { - this.onViewRefresh(view, view.getViewRange()); - } - }, - // Cell must be left blank - defaultRenderer: Ext.emptyFn, - updater: function(cell, value, record) { - this.updateWidget(record); - }, - onResize: function(newWidth) { - var me = this, - liveWidgets = me.liveWidgets, - view = me.getView(), - id, cell; - if (!me.isFixedSize && me.rendered && view && view.viewReady) { - cell = view.getEl().down(me.getCellInnerSelector()); - if (cell) { - // Subtract innerCell padding width - newWidth -= parseInt(me.getCachedStyle(cell, 'padding-left'), 10) + parseInt(me.getCachedStyle(cell, 'padding-right'), 10); - for (id in liveWidgets) { - liveWidgets[id].setWidth(newWidth); - } - } - } - }, - onAdded: function() { - var me = this, - view; - me.callParent(arguments); - view = me.getView(); - // If we are being added to a rendered HeaderContainer - if (view) { - me.setupViewListeners(view); - if (view && view.viewReady && me.rendered && view.getEl().down(me.getCellSelector())) { - // If the view is ready, it means we're already rendered. - // At this point the view may refresh "soon", however we don't have - // a way of knowing that the view is pending a refresh, so we need - // to ensure the widgets get hooked up correctly here - me.onViewRefresh(view, view.getViewRange()); - } - } - }, - onRemoved: function(isDestroying) { - var me = this, - liveWidgets = me.liveWidgets, - viewListeners = me.viewListeners, - id; - if (me.rendered) { - me.viewListeners = viewListeners && Ext.destroy(viewListeners); - // If we are being removed, we have to move all widget elements into the detached body - if (!isDestroying) { - for (id in liveWidgets) { - liveWidgets[id].detachFromBody(); - } - } - } - me.callParent(arguments); - }, - onDestroy: function() { - var me = this, - oldWidgetMap = me.liveWidgets, - freeWidgetStack = me.freeWidgetStack, - id, widget, i, len; - if (me.rendered) { - for (id in oldWidgetMap) { - widget = oldWidgetMap[id]; - widget.$widgetRecord = widget.$widgetColumn = null; - delete widget.getWidgetRecord; - delete widget.getWidgetColumn; - widget.destroy(); - } - for (i = 0 , len = freeWidgetStack.length; i < len; ++i) { - freeWidgetStack[i].destroy(); - } - } - me.freeWidgetStack = me.liveWidgets = null; - me.callParent(); - }, - getWidget: function(record) { - var liveWidgets = this.liveWidgets, - widget; - if (record && liveWidgets) { - widget = liveWidgets[record.internalId]; - } - return widget || null; - }, - privates: { - getCachedStyle: function(el, style) { - var cachedStyles = this.cachedStyles; - return cachedStyles[style] || (cachedStyles[style] = Ext.fly(el).getStyle(style)); - }, - getFreeWidget: function() { - var me = this, - result = me.freeWidgetStack ? me.freeWidgetStack.pop() : null; - if (!result) { - result = Ext.widget(me.widget); - result.resolveListenerScope = me.listenerScopeFn; - result.getWidgetRecord = me.widgetRecordDecorator; - result.getWidgetColumn = me.widgetColumnDecorator; - result.dataIndex = me.dataIndex; - result.measurer = me; - result.ownerCmp = me.getView(); - // The ownerCmp of the widget is the encapsulating view, which means it will be considered - // as a layout child, but it isn't really, we always need the layout on the - // component to run if asked. - result.isLayoutChild = me.returnFalse; - } - return result; - }, - onBeforeRefresh: function() { - var liveWidgets = this.liveWidgets, - id; - // Because of a memory leak bug in IE 8, we need to handle the dom node here before - // it is destroyed. - // See EXTJS-14874. - for (id in liveWidgets) { - liveWidgets[id].detachFromBody(); - } - }, - onItemAdd: function(records, index, items) { - var me = this, - view = me.getView(), - hasAttach = !!me.onWidgetAttach, - dataIndex = me.dataIndex, - isFixedSize = me.isFixedSize, - len = records.length, - i, record, row, cell, widget, el, focusEl, width; - // Loop through all records added, ensuring that our corresponding cell in each item - // has a Widget of the correct type in it, and is updated with the correct value from the record. - if (me.isVisible(true)) { - for (i = 0; i < len; i++) { - record = records[i]; - if (record.isNonData) { - - continue; - } - row = view.getRowFromItem(items[i]); - // May be a placeholder with no data row - if (row) { - cell = row.cells[me.getVisibleIndex()].firstChild; - if (!isFixedSize && !width) { - width = me.lastBox.width - parseInt(me.getCachedStyle(cell, 'padding-left'), 10) - parseInt(me.getCachedStyle(cell, 'padding-right'), 10); - } - widget = me.liveWidgets[record.internalId] = me.getFreeWidget(); - widget.$widgetColumn = me; - widget.$widgetRecord = record; - // Render/move a widget into the new row - Ext.fly(cell).empty(); - // Call the appropriate setter with this column's data field - if (widget.defaultBindProperty && dataIndex) { - widget.setConfig(widget.defaultBindProperty, record.get(dataIndex)); - } - if (hasAttach) { - Ext.callback(me.onWidgetAttach, me.scope, [ - me, - widget, - record - ], 0, me); - } - el = widget.el || widget.element; - if (el) { - cell.appendChild(el.dom); - if (!isFixedSize) { - widget.setWidth(width); - } - widget.reattachToBody(); - } else { - if (!isFixedSize) { - widget.width = width; - } - widget.render(cell); - } - // If the widget has a focusEl, ensure that its tabbability status is synched with the view's - // navigable/actionable state. - focusEl = widget.getFocusEl(); - if (focusEl) { - if (view.actionableMode) { - if (!focusEl.isTabbable()) { - focusEl.restoreTabbableState(); - } - } else { - if (focusEl.isTabbable()) { - focusEl.saveTabbableState(); - } - } - } - } - } - } - }, - onItemRemove: function(records, index, items) { - var me = this, - liveWidgets = me.liveWidgets, - widget, item, id, len, i, focusEl; - if (me.rendered) { - // Single item or Array. - items = Ext.Array.from(items); - len = items.length; - for (i = 0; i < len; i++) { - item = items[i]; - // If there was a record ID (collapsed placeholder will no longer be - // accessible)... return ousted widget to free stack, and move its element - // to the detached body - id = item.getAttribute('data-recordId'); - if (id && (widget = liveWidgets[id])) { - delete liveWidgets[id]; - me.freeWidgetStack.unshift(widget); - widget.$widgetRecord = widget.$widgetColumn = null; - // Focusables in a grid must not be tabbable by default when they get put back in. - focusEl = widget.getFocusEl(); - if (focusEl) { - // Widgets are reused so we must reset their tabbable state - // regardless of their visibility. - // For example, when removing rows in IE8 we're attaching - // the nodes to a document-fragment which itself is invisible, - // so isTabbable() returns false. Next time when we're reusing - // this widget it will be attached to the document with its - // tabbable state unreset, which might lead to undesired results. - if (focusEl.isTabbable(true)) { - focusEl.saveTabbableState({ - includeHidden: true - }); - } - // Some browsers do not deliver a focus change upon DOM removal. - // Force the issue here. - focusEl.blur(); - } - widget.detachFromBody(); - } - } - } - }, - onItemUpdate: function(record, recordIndex, oldItemDom) { - this.updateWidget(record); - }, - onViewRefresh: function(view, records) { - var me = this, - rows = view.all, - hasAttach = !!me.onWidgetAttach, - oldWidgetMap = me.liveWidgets, - dataIndex = me.dataIndex, - isFixedSize = me.isFixedSize, - cell, widget, el, width, recordId, itemIndex, recordIndex, record, id, lastBox, dom; - if (me.isVisible(true)) { - me.liveWidgets = {}; - Ext.suspendLayouts(); - for (itemIndex = rows.startIndex , recordIndex = 0; itemIndex <= rows.endIndex; itemIndex++ , recordIndex++) { - record = records[recordIndex]; - if (record.isNonData) { - - continue; - } - recordId = record.internalId; - cell = view.getRow(rows.item(itemIndex)).cells[me.getVisibleIndex()].firstChild; - // Attempt to reuse the existing widget for this record. - widget = me.liveWidgets[recordId] = oldWidgetMap[recordId] || me.getFreeWidget(); - widget.$widgetRecord = record; - widget.$widgetColumn = me; - delete oldWidgetMap[recordId]; - lastBox = me.lastBox; - if (lastBox && !isFixedSize && width === undefined) { - width = lastBox.width - parseInt(me.getCachedStyle(cell, 'padding-left'), 10) - parseInt(me.getCachedStyle(cell, 'padding-right'), 10); - } - // Call the appropriate setter with this column's data field - if (widget.defaultBindProperty && dataIndex) { - widget.setConfig(widget.defaultBindProperty, records[recordIndex].get(dataIndex)); - } - if (hasAttach) { - Ext.callback(me.onWidgetAttach, me.scope, [ - me, - widget, - record - ], 0, me); - } - el = widget.el || widget.element; - if (el) { - dom = el.dom; - if (dom.parentNode !== cell) { - Ext.fly(cell).empty(); - cell.appendChild(el.dom); - } - if (!isFixedSize) { - widget.setWidth(width); - } - widget.reattachToBody(); - } else { - if (!isFixedSize) { - widget.width = width; - } - Ext.fly(cell).empty(); - widget.render(cell); - } - } - Ext.resumeLayouts(true); - // Free any unused widgets from the old live map. - // Move them into detachedBody. - for (id in oldWidgetMap) { - widget = oldWidgetMap[id]; - widget.$widgetRecord = widget.$widgetColumn = null; - me.freeWidgetStack.unshift(widget); - widget.detachFromBody(); - } - } - }, - returnFalse: function() { - return false; - }, - setupViewListeners: function(view) { - var me = this; - me.viewListeners = view.on({ - refresh: me.onViewRefresh, - itemupdate: me.onItemUpdate, - itemadd: me.onItemAdd, - itemremove: me.onItemRemove, - scope: me, - destroyable: true - }); - if (Ext.isIE8) { - view.on('beforerefresh', me.onBeforeRefresh, me); - } - }, - updateWidget: function(record) { - var dataIndex = this.dataIndex, - widget; - if (this.rendered) { - widget = this.liveWidgets[record.internalId]; - // Call the appropriate setter with this column's data field - if (widget && widget.defaultBindProperty && dataIndex) { - widget.setConfig(widget.defaultBindProperty, record.get(dataIndex)); - } - } - }, - widgetRecordDecorator: function() { - return this.$widgetRecord; - }, - widgetColumnDecorator: function() { - return this.$widgetColumn; - } - } -}); - -/** - * A feature is a type of plugin that is specific to the {@link Ext.grid.Panel}. It provides several - * hooks that allows the developer to inject additional functionality at certain points throughout the - * grid creation cycle. This class provides the base template methods that are available to the developer, - * it should be extended. - * - * There are several built in features that extend this class, for example: - * - * - {@link Ext.grid.feature.Grouping} - Shows grid rows in groups as specified by the {@link Ext.data.Store} - * - {@link Ext.grid.feature.RowBody} - Adds a body section for each grid row that can contain markup. - * - {@link Ext.grid.feature.Summary} - Adds a summary row at the bottom of the grid with aggregate totals for a column. - * - * ## Using Features - * A feature is added to the grid by specifying it an array of features in the configuration: - * - * var groupingFeature = Ext.create('Ext.grid.feature.Grouping'); - * Ext.create('Ext.grid.Panel', { - * // other options - * features: [groupingFeature] - * }); - * - * ## Writing Features - * - * A Feature may add new DOM structure within the structure of a grid. - * - * A grid is essentially a `
    ` element. A {@link Ext.view.Table TableView} instance uses four {@link Ext.XTemplate XTemplates} - * to render the grid, `tpl`, `itemTpl`, `rowTpl`, `cellTpl`. - * - * A {@link Ext.view.Table TableView} uses its `tpl` to emit the item encapsulating HTML tags into its output stream. - * To render the rows, it invokes {@link Ext.view.Table#renderRows} passing the `rows` member of its data object and the `columns` member of its data object. - * - * The `tpl`'s data object Looks like this: - * { - * view: owningTableView, - * rows: recordsToRender, - * viewStartIndex: indexOfFirstRecordInStore, - * tableStyle: styleString - * } - * - * * A {@link Ext.view.Table TableView} uses its `rowTpl` to emit a `` HTML tag to its output stream. To render cells, - * it invokes {@link Ext.view.Table#renderCell} passing the `rows` member of its data object. - * - * The `itemTpl` and `rowTpl`'s data object looks like this: - * - * { - * view: owningTableView, - * record: recordToRender, - * recordIndex: indexOfRecordInStore, - * rowIndex: indexOfRowInView, - * columns: arrayOfColumnDefinitions, - * itemClasses: arrayOfClassNames, // For outermost row in case of wrapping - * rowClasses: arrayOfClassNames, // For internal data bearing row in case of wrapping - * rowStyle: styleString - * } - * - * * A {@link Ext.view.Table TableView} uses its `cellTpl` to emit a `- // Some browsers use content box and some use border box when applying the style width of a TD - if (!me.summaryTableCls) { - me.summaryTableCls = Ext.baseCSSPrefix + 'grid-item'; - } - me.summaryRowSelector = '.' + me.summaryRowCls; - }, - bindStore: function(grid, store) { - var me = this; - Ext.destroy(me.readerListeners); - if (me.remoteRoot) { - me.readerListeners = store.getProxy().getReader().on({ - scope: me, - destroyable: true, - rawdata: me.onReaderRawData - }); - } - }, - onReaderRawData: function(data) { - // Invalidate potentially existing summaryRows to force recalculation - this.summaryRows = null; - this.readerRawData = data; - }, - /** - * Toggle whether or not to show the summary row. - * @param {Boolean} visible True to show the summary row - */ - toggleSummaryRow: function(visible, /* private */ - fromLockingPartner) { - var me = this, - prev = me.showSummaryRow, - doRefresh; - visible = visible != null ? !!visible : !me.showSummaryRow; - me.showSummaryRow = visible; - if (visible && visible !== prev) { - // If being shown, something may have changed while not visible, so - // force the summary records to recalculate - me.updateSummaryRow = true; - } - // If there is another side to be toggled, then toggle it (as long as we are not already being commanded from that other side); - // Then refresh the whole arrangement. - if (me.lockingPartner) { - if (!fromLockingPartner) { - me.lockingPartner.toggleSummaryRow(visible, true); - doRefresh = true; - } - } else { - doRefresh = true; - } - if (doRefresh) { - me.grid.ownerGrid.getView().refresh(); - } - }, - createRenderer: function(column, record) { - var me = this, - ownerGroup = record.ownerGroup, - summaryData = ownerGroup ? me.summaryData[ownerGroup] : me.summaryData, - // Use the column.getItemId() for columns without a dataIndex. The populateRecord method does the same. - dataIndex = column.dataIndex || column.getItemId(); - return function(value, metaData) { - return column.summaryRenderer ? column.summaryRenderer(record.data[dataIndex], summaryData, dataIndex, metaData) : // For no summaryRenderer, return the field value in the Feature record. - record.data[dataIndex]; - }; - }, - outputSummaryRecord: function(summaryRecord, contextValues, out) { - var view = contextValues.view, - savedRowValues = view.rowValues, - columns = contextValues.columns || view.headerCt.getVisibleGridColumns(), - colCount = columns.length, - i, column, - // Set up a row rendering values object so that we can call the rowTpl directly to inject - // the markup of a grid row into the output stream. - values = { - view: view, - record: summaryRecord, - rowStyle: '', - rowClasses: [ - this.summaryRowCls - ], - itemClasses: [], - recordIndex: -1, - rowId: view.getRowId(summaryRecord), - columns: columns - }; - // Because we are using the regular row rendering pathway, temporarily swap out the renderer for the summaryRenderer - for (i = 0; i < colCount; i++) { - column = columns[i]; - column.savedRenderer = column.renderer; - if (column.summaryType || column.summaryRenderer) { - column.renderer = this.createRenderer(column, summaryRecord); - } else { - column.renderer = Ext.emptyFn; - } - } - // Use the base template to render a summary row - view.rowValues = values; - view.self.prototype.rowTpl.applyOut(values, out, parent); - view.rowValues = savedRowValues; - // Restore regular column renderers - for (i = 0; i < colCount; i++) { - column = columns[i]; - column.renderer = column.savedRenderer; - column.savedRenderer = null; - } - }, - /** - * Get the summary data for a field. - * @private - * @param {Ext.data.Store} store The store to get the data from - * @param {String/Function} type The type of aggregation. If a function is specified it will - * be passed to the stores aggregate function. - * @param {String} field The field to aggregate on - * @param {Boolean} group True to aggregate in grouped mode - * @return {Number/String/Object} See the return type for the store functions. - * if the group parameter is `true` An object is returned with a property named for each group who's - * value is the summary value. - */ - getSummary: function(store, type, field, group) { - var isGrouped = !!group, - item = isGrouped ? group : store; - if (type) { - if (Ext.isFunction(type)) { - if (isGrouped) { - return item.aggregate(field, type); - } else { - return item.aggregate(type, null, false, [ - field - ]); - } - } - switch (type) { - case 'count': - return item.count(field); - case 'min': - return item.min(field); - case 'max': - return item.max(field); - case 'sum': - return item.sum(field); - case 'average': - return item.average(field); - default: - return ''; - } - } - }, - getRawData: function() { - var data = this.readerRawData; - if (data) { - return data; - } - // Synchronous Proxies such as Memory proxy will set keepRawData to true - // on their Reader instances, and may have been loaded before we were bound - // to the store. Or the Reader may have been configured with keepRawData: true - // manually. - // In these cases, the Reader should have rawData on the instance. - return this.view.getStore().getProxy().getReader().rawData; - }, - generateSummaryData: function(groupField) { - var me = this, - summaryRows = me.summaryRows, - convertedSummaryRow = {}, - remoteData = {}, - storeReader, reader, rawData, i, len, summaryRows, rows, row; - // Summary rows may have been cached by previous run - if (!summaryRows) { - rawData = me.getRawData(); - if (!rawData) { - return; - } - // Construct a new Reader instance of the same type to avoid - // munging the one in the Store - storeReader = me.view.store.getProxy().getReader(); - reader = Ext.create('reader.' + storeReader.type, storeReader.getConfig()); - // reset reader root and rebuild extractors to extract summaries data - reader.setRootProperty(me.remoteRoot); - // At this point summaryRows is still raw data, e.g. XML node - summaryRows = reader.getRoot(rawData); - if (summaryRows) { - rows = []; - if (!Ext.isArray(summaryRows)) { - summaryRows = [ - summaryRows - ]; - } - len = summaryRows.length; - for (i = 0; i < len; ++i) { - // Convert a raw data row into a Record's hash object using the Reader. - row = reader.extractRecordData(summaryRows[i], me.readDataOptions); - rows.push(row); - } - me.summaryRows = summaryRows = rows; - } - // By the next time the configuration may change - reader.destroy(); - // We also no longer need the whole raw dataset - me.readerRawData = null; - } - if (summaryRows) { - for (i = 0 , len = summaryRows.length; i < len; i++) { - convertedSummaryRow = summaryRows[i]; - if (groupField) { - remoteData[convertedSummaryRow[groupField]] = convertedSummaryRow; - } - } - } - return groupField ? remoteData : convertedSummaryRow; - }, - setSummaryData: function(record, colId, summaryValue, groupName) { - var summaryData = this.summaryData; - if (groupName) { - if (!summaryData[groupName]) { - summaryData[groupName] = {}; - } - summaryData[groupName][colId] = summaryValue; - } else { - summaryData[colId] = summaryValue; - } - }, - destroy: function() { - Ext.destroy(this.readerListeners); - this.readerRawData = this.summaryRows = null; - this.callParent(); - } -}); - -/** - * Private record store class which takes the place of the view's data store to provide a grouped - * view of the data when the Grouping feature is used. - * - * Relays granular mutation events from the underlying store as refresh events to the view. - * - * On mutation events from the underlying store, updates the summary rows by firing update events on the corresponding - * summary records. - * @private - */ -Ext.define('Ext.grid.feature.GroupStore', { - extend: 'Ext.util.Observable', - isStore: true, - // Number of records to load into a buffered grid before it has been bound to a view of known size - defaultViewSize: 100, - // Use this property moving forward for all feature stores. It will be used to ensure - // that the correct object is used to call various APIs. See EXTJSIV-10022. - isFeatureStore: true, - badGrouperKey: '[object Object]', - constructor: function(groupingFeature, store) { - var me = this; - me.callParent(); - me.groupingFeature = groupingFeature; - me.bindStore(store); - // We don't want to listen to store events in a locking assembly. - if (!groupingFeature.grid.isLocked) { - me.bindViewStoreListeners(); - } - }, - bindStore: function(store) { - var me = this; - if (!store || me.store !== store) { - Ext.destroy(me.storeListeners); - me.store = null; - } - if (store) { - me.storeListeners = store.on({ - groupchange: me.onGroupChange, - remove: me.onRemove, - add: me.onAdd, - idchanged: me.onIdChanged, - update: me.onUpdate, - refresh: me.onRefresh, - clear: me.onClear, - scope: me, - destroyable: true - }); - me.store = store; - me.processStore(store); - } - }, - bindViewStoreListeners: function() { - var view = this.groupingFeature.view, - listeners = view.getStoreListeners(); - listeners.scope = view; - this.on(listeners); - }, - processStore: function(store) { - var me = this, - groupingFeature = me.groupingFeature, - collapseAll = groupingFeature.startCollapsed, - data = me.data, - ExtArray = Ext.Array, - indexOf = ExtArray.indexOf, - splice = ExtArray.splice, - groups = store.getGroups(), - groupCount = groups ? groups.length : 0, - groupField = store.getGroupField(), - // We need to know all of the possible unique group names. The only way to know this is to check itemGroupKeys, which will keep a - // list of all potential group names. It's not enough to get the key of the existing groups since the collection may be filtered. - groupNames = groups && ExtArray.unique(Ext.Object.getValues(groups.itemGroupKeys)), - isCollapsed = false, - oldMetaGroupCache = groupingFeature.getCache(), - metaGroup, metaGroupCache, i, len, featureGrouper, group, groupName, groupPlaceholder, key, modelData, Model; - groupingFeature.invalidateCache(); - // Get a new cache since we invalidated the old one. - metaGroupCache = groupingFeature.getCache(); - // Persist what we can. - if (oldMetaGroupCache.map) { - metaGroupCache.map = oldMetaGroupCache.map; - } - if (data) { - data.clear(); - } else { - data = me.data = new Ext.util.Collection({ - rootProperty: 'data', - extraKeys: { - byInternalId: { - property: 'internalId', - rootProperty: '' - } - } - }); - } - if (store.getCount()) { - // Upon first process of a loaded store, clear the "always" collapse" flag - groupingFeature.startCollapsed = false; - if (groupCount > 0) { - Model = store.getModel(); - for (i = 0; i < groupCount; i++) { - group = groups.getAt(i); - // Cache group information by group name. - key = group.getGroupKey(); - // If there is no store grouper and the groupField looks up a complex data type, the store will stringify it and - // the group name will be '[object Object]'. To fix this, groupers can be defined in the feature config, so we'll - // simply do a lookup here and re-group the store. - // - // Note that if a grouper wasn't defined on the feature that we'll just default to the old behavior and still try - // to group. - if (me.badGrouperKey === key && (featureGrouper = groupingFeature.getGrouper(groupField))) { - // We must reset the value b/c store.group() will call into this method again! - groupingFeature.startCollapsed = collapseAll; - store.group(featureGrouper); - return; - } - // Persist what we can. - metaGroup = metaGroupCache[key] = oldMetaGroupCache[key] || groupingFeature.getMetaGroup(key); - // Remove the group name from the list of all possible group names. This is how we'll know if any remaining groups - // in the old cache should be retained. - splice(groupNames, indexOf(groupNames, key), 1); - isCollapsed = metaGroup.isCollapsed = collapseAll || metaGroup.isCollapsed; - // If group is collapsed, then represent it by one dummy row which is never visible, but which acts - // as a start and end group trigger. - if (isCollapsed) { - modelData = {}; - modelData[groupField] = key; - metaGroup.placeholder = groupPlaceholder = new Model(modelData); - groupPlaceholder.isNonData = groupPlaceholder.isCollapsedPlaceholder = true; - groupPlaceholder.group = group; - data.add(groupPlaceholder); - } else // Expanded group - add the group's child records. - { - data.insert(me.data.length, group.items); - } - } - if (groupNames.length) { - // The remainig group names in this list may refer to potential groups that have been filtered/sorted. If the group - // name exists in the old cache, we must retain it b/c the groups could be recreated. See EXTJS-15755 for an example. - // Anything left in the old cache can be discarded. - for (i = 0 , len = groupNames.length; i < len; i++) { - groupName = groupNames[i]; - metaGroupCache[groupName] = oldMetaGroupCache[groupName]; - } - } - oldMetaGroupCache = null; - } else { - data.add(store.getRange()); - } - } - }, - isCollapsed: function(name) { - return this.groupingFeature.getCache()[name].isCollapsed; - }, - isLoading: function() { - return false; - }, - getData: function() { - return this.data; - }, - getCount: function() { - return this.data.getCount(); - }, - getTotalCount: function() { - return this.data.getCount(); - }, - // This class is only created for fully loaded, non-buffered stores - rangeCached: function(start, end) { - return end < this.getCount(); - }, - getRange: function(start, end, options) { - // Collection's getRange is exclusive. Do NOT mutate the value: it is passed to the callback. - var result = this.data.getRange(start, Ext.isNumber(end) ? end + 1 : end); - if (options && options.callback) { - options.callback.call(options.scope || this, result, start, end, options); - } - return result; - }, - getAt: function(index) { - return this.data.getAt(index); - }, - /** - * Get the Record with the specified id. - * - * This method is not affected by filtering, lookup will be performed from all records - * inside the store, filtered or not. - * - * @param {Mixed} id The id of the Record to find. - * @return {Ext.data.Model} The Record with the passed id. Returns null if not found. - */ - getById: function(id) { - return this.store.getById(id); - }, - /** - * @private - * Get the Record with the specified internalId. - * - * This method is not effected by filtering, lookup will be performed from all records - * inside the store, filtered or not. - * - * @param {Mixed} internalId The id of the Record to find. - * @return {Ext.data.Model} The Record with the passed internalId. Returns null if not found. - */ - getByInternalId: function(internalId) { - // Find the record in the base store. - // If it was a placeholder, then it won't be there, it will be in our data Collection. - return this.store.getByInternalId(internalId) || this.data.byInternalId.get(internalId); - }, - expandGroup: function(group) { - var me = this, - groupingFeature = me.groupingFeature, - metaGroup, placeholder, startIdx, items; - if (typeof group === 'string') { - group = groupingFeature.getGroup(group); - } - if (group) { - items = group.items; - metaGroup = groupingFeature.getMetaGroup(group); - placeholder = metaGroup.placeholder; - } - if (items.length && (startIdx = me.data.indexOf(placeholder)) !== -1) { - // Any event handlers must see the new state - metaGroup.isCollapsed = false; - me.isExpandingOrCollapsing = 1; - // Remove the collapsed group placeholder record - me.data.removeAt(startIdx); - // Insert the child records in its place - me.data.insert(startIdx, group.items); - // Update views - me.fireEvent('replace', me, startIdx, [ - placeholder - ], group.items); - me.fireEvent('groupexpand', me, group); - me.isExpandingOrCollapsing = 0; - } - }, - collapseGroup: function(group) { - var me = this, - groupingFeature = me.groupingFeature, - startIdx, placeholder, len, items; - if (typeof group === 'string') { - group = groupingFeature.getGroup(group); - } - if (group) { - items = group.items; - } - if (items && (len = items.length) && (startIdx = me.data.indexOf(items[0])) !== -1) { - // Any event handlers must see the new state - groupingFeature.getMetaGroup(group).isCollapsed = true; - me.isExpandingOrCollapsing = 2; - // Remove the group child records - me.data.removeAt(startIdx, len); - // Insert a placeholder record in their place - me.data.insert(startIdx, placeholder = me.getGroupPlaceholder(group)); - // Update views - me.fireEvent('replace', me, startIdx, items, [ - placeholder - ]); - me.fireEvent('groupcollapse', me, group); - me.isExpandingOrCollapsing = 0; - } - }, - getGroupPlaceholder: function(group) { - var metaGroup = this.groupingFeature.getMetaGroup(group); - if (!metaGroup.placeholder) { - var store = this.store, - Model = store.getModel(), - modelData = {}, - key = group.getGroupKey(), - groupPlaceholder; - modelData[store.getGroupField()] = key; - groupPlaceholder = metaGroup.placeholder = new Model(modelData); - groupPlaceholder.isNonData = groupPlaceholder.isCollapsedPlaceholder = true; - // Let's poke the groupKey onto the record instead of storing a reference to the group - // itself. The latter can cause problems if the store is reloaded and the referenced - // group is lost. - // See EXTJS-18655 - groupPlaceholder.groupKey = key; - } - return metaGroup.placeholder; - }, - // Find index of record in group store. - // If it's in a collapsed group, then it's -1, not present - indexOf: function(record) { - var ret = -1; - if (!record.isCollapsedPlaceholder) { - ret = this.data.indexOf(record); - } - return ret; - }, - contains: function(record) { - return this.indexOf(record) > -1; - }, - indexOfPlaceholder: function(record) { - return this.data.indexOf(record); - }, - /** - * Get the index within the store of the Record with the passed id. - * - * Like #indexOf, this method is effected by filtering. - * - * @param {String} id The id of the Record to find. - * @return {Number} The index of the Record. Returns -1 if not found. - */ - indexOfId: function(id) { - return this.data.indexOfKey(id); - }, - /** - * Get the index within the entire dataset. From 0 to the totalCount. - * - * Like #indexOf, this method is effected by filtering. - * - * @param {Ext.data.Model} record The Ext.data.Model object to find. - * @return {Number} The index of the passed Record. Returns -1 if not found. - */ - indexOfTotal: function(record) { - return this.store.indexOf(record); - }, - onAdd: function(store) { - var me = this; - me.processStore(me.store); - me.fireEvent('refresh', me); - // Don't allow the event to propagate or another group will be added upstream by tableview! - return false; - }, - onClear: function(store, records, startIndex) { - var me = this; - me.processStore(me.store); - me.fireEvent('clear', me); - }, - onIdChanged: function(store, rec, oldId, newId) { - this.data.updateKey(rec, oldId); - }, - onRefresh: function() { - this.processStore(this.store); - this.fireEvent('refresh', this); - }, - onRemove: function() { - var me = this; - me.processStore(me.store); - me.fireEvent('refresh', me); - // Don't allow the event to propagate or the view will not be fully updated. - return false; - }, - onUpdate: function(store, record, operation, modifiedFieldNames) { - var me = this, - groupingFeature = me.groupingFeature, - group, metaGroup, firstRec, lastRec, items; - // The grouping field value has been modified. - // This could either move a record from one group to another, or introduce a new group. - // Either way, we have to refresh the grid - if (store.isGrouped()) { - // Updating a single record, attach the group to the record for Grouping.setupRowData to use. - group = record.group = groupingFeature.getGroup(record); - // Make sure that still we have a group and that the last member of it wasn't just filtered. - // See EXTJS-18083. - if (group) { - metaGroup = groupingFeature.getMetaGroup(record); - if (modifiedFieldNames && Ext.Array.contains(modifiedFieldNames, groupingFeature.getGroupField())) { - return me.onRefresh(me.store); - } - // Fire an update event on the collapsed metaGroup placeholder record - if (metaGroup.isCollapsed) { - me.fireEvent('update', me, metaGroup.placeholder); - } else // Not in a collapsed group, fire update event on the modified record - // and, if in a grouped store, on the first and last records in the group. - { - Ext.suspendLayouts(); - // Propagate the record's update event - me.fireEvent('update', me, record, operation, modifiedFieldNames); - // Fire update event on first and last record in group (only once if a single row group) - // So that custom header TPL is applied, and the summary row is updated - items = group.items; - firstRec = items[0]; - lastRec = items[items.length - 1]; - // Fire an update on the first and last row in the group (ensure we don't refire update on the modified record). - // This is to give interested Features the opportunity to update the first item (a wrapped group header + data row), - // and last item (a wrapped data row + group summary) - if (firstRec !== record) { - firstRec.group = group; - me.fireEvent('update', me, firstRec, 'edit', modifiedFieldNames); - delete firstRec.group; - } - if (lastRec !== record && lastRec !== firstRec && groupingFeature.showSummaryRow) { - lastRec.group = group; - me.fireEvent('update', me, lastRec, 'edit', modifiedFieldNames); - delete lastRec.group; - } - Ext.resumeLayouts(true); - } - } - delete record.group; - } else { - // Propagate the record's update event - me.fireEvent('update', me, record, operation, modifiedFieldNames); - } - }, - // Relay the groupchange event - onGroupChange: function(store, grouper) { - if (!grouper) { - this.processStore(store); - } - this.fireEvent('groupchange', store, grouper); - }, - destroy: function() { - var me = this; - me.bindStore(null); - Ext.destroyMembers(me, 'data', 'groupingFeature'); - me.callParent(); - } -}); - -/** - * This feature allows to display the grid rows aggregated into groups as specified by the {@link Ext.data.Store#grouper grouper} - * - * underneath. The groups can also be expanded and collapsed. - * - * ## Extra Events - * - * This feature adds several extra events that will be fired on the grid to interact with the groups: - * - * - {@link #groupclick} - * - {@link #groupdblclick} - * - {@link #groupcontextmenu} - * - {@link #groupexpand} - * - {@link #groupcollapse} - * - * ## Menu Augmentation - * - * This feature adds extra options to the grid column menu to provide the user with functionality to modify the grouping. - * This can be disabled by setting the {@link #enableGroupingMenu} option. The option to disallow grouping from being turned off - * by the user is {@link #enableNoGroups}. - * - * ## Controlling Group Text - * - * The {@link #groupHeaderTpl} is used to control the rendered title for each group. It can modified to customized - * the default display. - * - * ## Groupers - * - * By default, this feature expects that the data field that is mapped to by the - * {@link Ext.data.AbstractStore#groupField} config is a simple data type such as a - * String or a Boolean. However, if you intend to group by a data field that is a - * complex data type such as an Object or Array, it is necessary to define one or more - * {@link Ext.util.Grouper groupers} on the feature that it can then use to lookup - * internal group information when grouping by different fields. - * - * @example - * var feature = Ext.create('Ext.grid.feature.Grouping', { - * startCollapsed: true, - * groupers: [{ - * property: 'asset', - * groupFn: function (val) { - * return val.data.name; - * } - * }] - * }); - * - * ## Example Usage - * - * @example - * var store = Ext.create('Ext.data.Store', { - * fields: ['name', 'seniority', 'department'], - * groupField: 'department', - * data: [ - * { name: 'Michael Scott', seniority: 7, department: 'Management' }, - * { name: 'Dwight Schrute', seniority: 2, department: 'Sales' }, - * { name: 'Jim Halpert', seniority: 3, department: 'Sales' }, - * { name: 'Kevin Malone', seniority: 4, department: 'Accounting' }, - * { name: 'Angela Martin', seniority: 5, department: 'Accounting' } - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Employees', - * store: store, - * columns: [ - * { text: 'Name', dataIndex: 'name' }, - * { text: 'Seniority', dataIndex: 'seniority' } - * ], - * features: [{ftype:'grouping'}], - * width: 200, - * height: 275, - * renderTo: Ext.getBody() - * }); - * - * **Note:** To use grouping with a grid that has {@link Ext.grid.column.Column#locked locked columns}, you need to supply - * the grouping feature as a config object - so the grid can create two instances of the grouping feature. - */ -Ext.define('Ext.grid.feature.Grouping', { - extend: 'Ext.grid.feature.Feature', - mixins: { - summary: 'Ext.grid.feature.AbstractSummary' - }, - requires: [ - 'Ext.grid.feature.GroupStore' - ], - alias: 'feature.grouping', - eventPrefix: 'group', - eventSelector: '.' + Ext.baseCSSPrefix + 'grid-group-hd', - refreshData: {}, - wrapsItem: true, - /** - * @event groupclick - * @param {Ext.view.Table} view - * @param {HTMLElement} node - * @param {String} group The name of the group - * @param {Ext.event.Event} e - */ - /** - * @event groupdblclick - * @param {Ext.view.Table} view - * @param {HTMLElement} node - * @param {String} group The name of the group - * @param {Ext.event.Event} e - */ - /** - * @event groupcontextmenu - * @param {Ext.view.Table} view - * @param {HTMLElement} node - * @param {String} group The name of the group - * @param {Ext.event.Event} e - */ - /** - * @event groupcollapse - * @param {Ext.view.Table} view - * @param {HTMLElement} node - * @param {String} group The name of the group - */ - /** - * @event groupexpand - * @param {Ext.view.Table} view - * @param {HTMLElement} node - * @param {String} group The name of the group - */ - /** - * @cfg {String/Array/Ext.Template} groupHeaderTpl - * A string Template snippet, an array of strings (optionally followed by an object containing Template methods) to be used to construct a Template, or a Template instance. - * - * - Example 1 (Template snippet): - * - * groupHeaderTpl: 'Group: {name}' - * - * - Example 2 (Array): - * - * groupHeaderTpl: [ - * 'Group: ', - * '
    {name:this.formatName}
    ', - * { - * formatName: function(name) { - * return Ext.String.trim(name); - * } - * } - * ] - * - * - Example 3 (Template Instance): - * - * groupHeaderTpl: Ext.create('Ext.XTemplate', - * 'Group: ', - * '
    {name:this.formatName}
    ', - * { - * formatName: function(name) { - * return Ext.String.trim(name); - * } - * } - * ) - * - * @cfg {String} groupHeaderTpl.groupField The field name being grouped by. - * @cfg {String} groupHeaderTpl.columnName The column header associated with the field being grouped by *if there is a column for the field*, falls back to the groupField name. - * @cfg {Mixed} groupHeaderTpl.groupValue The value of the {@link Ext.data.Store#groupField groupField} for the group header being rendered. - * @cfg {String} groupHeaderTpl.renderedGroupValue The rendered value of the {@link Ext.data.Store#groupField groupField} for the group header being rendered, as produced by the column renderer. - * @cfg {String} groupHeaderTpl.name An alias for renderedGroupValue - * @cfg {Ext.data.Model[]} groupHeaderTpl.rows Deprecated - use children instead. An array containing the child records for the group being rendered. *Not available if the store is a {@link Ext.data.BufferedStore BufferedStore}* - * @cfg {Ext.data.Model[]} groupHeaderTpl.children An array containing the child records for the group being rendered. *Not available if the store is a {@link Ext.data.BufferedStore BufferedStore}* - */ - groupHeaderTpl: '{columnName}: {name}', - /** - * @cfg {Number} [depthToIndent=17] - * Number of pixels to indent per grouping level - */ - depthToIndent: 17, - collapsedCls: Ext.baseCSSPrefix + 'grid-group-collapsed', - hdCollapsedCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsed', - hdNotCollapsibleCls: Ext.baseCSSPrefix + 'grid-group-hd-not-collapsible', - collapsibleCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsible', - ctCls: Ext.baseCSSPrefix + 'group-hd-container', - // - /** - * @cfg {String} [groupByText="Group by this field"] - * Text displayed in the grid header menu for grouping by header. - */ - groupByText: 'Group by this field', - // - // - /** - * @cfg {String} [showGroupsText="Show in groups"] - * Text displayed in the grid header for enabling/disabling grouping. - */ - showGroupsText: 'Show in groups', - // - /** - * @cfg {Boolean} [hideGroupedHeader=false] - * True to hide the header that is currently grouped. - */ - hideGroupedHeader: false, - /** - * @cfg {Boolean} [startCollapsed=false] - * True to start all groups collapsed. - */ - startCollapsed: false, - /** - * @cfg {Boolean} [enableGroupingMenu=true] - * True to enable the grouping control in the header menu. - */ - enableGroupingMenu: true, - /** - * @cfg {Boolean} [enableNoGroups=true] - * True to allow the user to turn off grouping. - */ - enableNoGroups: true, - /** - * @cfg {Boolean} [collapsible=true] - * Set to `false` to disable collapsing groups from the UI. - * - * This is set to `false` when the associated {@link Ext.data.Store store} is - * a {@link Ext.data.BufferedStore BufferedStore}. - */ - collapsible: true, - /** - * @cfg {Array} [groupers=null] - * These are grouper objects defined for the feature. If the group names are derived - * from complex data types, it is necessary to convert them as a store would. - * - * However, since only one grouper can be defined on the store at a time and - * this feature clears the current grouper when a new one is added, it is - * necessary to define a cache of groupers that the feature can lookup as needed. - * - * Expected grouper object properties are `property` and `groupFn`. - */ - groupers: null, - // - expandTip: 'Click to expand. CTRL key collapses all others', - // - // - collapseTip: 'Click to collapse. CTRL/click collapses all others', - // - showSummaryRow: false, - outerTpl: [ - '{%', - // Set up the grouping unless we are disabled, or it's just a summary record - 'if (!(this.groupingFeature.disabled || values.rows.length === 1 && values.rows[0].isSummary)) {', - 'this.groupingFeature.setup(values.rows, values.view.rowValues);', - '}', - // Process the item - 'this.nextTpl.applyOut(values, out, parent);', - // Clean up the grouping unless we are disabled, or it's just a summary record - 'if (!(this.groupingFeature.disabled || values.rows.length === 1 && values.rows[0].isSummary)) {', - 'this.groupingFeature.cleanup(values.rows, values.view.rowValues);', - '}', - '%}', - { - priority: 200 - } - ], - groupRowTpl: [ - '{%', - 'var me = this.groupingFeature,', - 'colspan = "colspan=" + values.columns.length;', - // If grouping is disabled or it's just a summary record, do not call setupRowData, and do not wrap - 'if (me.disabled || parent.rows.length === 1 && parent.rows[0].isSummary) {', - 'values.needsWrap = false;', - '} else {', - // setupRowData requires the index in the data source, not the index in the real store - 'me.setupRowData(values.record, values.rowIndex, values);', - '}', - '%}', - '', - '', - // MUST output column sizing elements because the first row in this table - // contains one colspanning TD, and that overrides subsequent column width settings. - '{% values.view.renderColumnSizer(values, out); %}', - '', - '', - '', - '', - // Only output the first row if this is *not* a collapsed group - '', - '{%', - 'values.itemClasses.length = 0;', - 'this.nextTpl.applyOut(values, out, parent);', - '%}', - '', - '', - '{%me.outputSummaryRecord(values.summaryRecord, values, out, parent);%}', - '', - '', - '{%this.nextTpl.applyOut(values, out, parent);%}', - '', - { - priority: 200, - beginRowSync: function(rowSync) { - var groupingFeature = this.groupingFeature; - rowSync.add('header', groupingFeature.eventSelector); - rowSync.add('summary', groupingFeature.summaryRowSelector); - }, - syncContent: function(destRow, sourceRow, columnsToUpdate) { - destRow = Ext.fly(destRow, 'syncDest'); - sourceRow = Ext.fly(sourceRow, 'syncSrc'); - var groupingFeature = this.groupingFeature, - destHd = destRow.down(groupingFeature.eventSelector, true), - sourceHd = sourceRow.down(groupingFeature.eventSelector, true), - destSummaryRow = destRow.down(groupingFeature.summaryRowSelector, true), - sourceSummaryRow = sourceRow.down(groupingFeature.summaryRowSelector, true); - // Sync the content of header element. - if (destHd && sourceHd) { - Ext.fly(destHd).syncContent(sourceHd); - } - // Sync just the updated columns in the summary row. - if (destSummaryRow && sourceSummaryRow) { - // If we were passed a column set, only update them - if (columnsToUpdate) { - this.groupingFeature.view.updateColumns(destSummaryRow, sourceSummaryRow, columnsToUpdate); - } else { - Ext.fly(destSummaryRow).syncContent(sourceSummaryRow); - } - } - } - } - ], - init: function(grid) { - var me = this, - view = me.view, - store = me.getGridStore(), - lockPartner, dataSource; - view.isGrouping = store.isGrouped(); - me.mixins.summary.init.call(me); - me.callParent([ - grid - ]); - view.headerCt.on({ - columnhide: me.onColumnHideShow, - columnshow: me.onColumnHideShow, - columnmove: me.onColumnMove, - scope: me - }); - // Add a table level processor - view.addTpl(Ext.XTemplate.getTpl(me, 'outerTpl')).groupingFeature = me; - // Add a row level processor - view.addRowTpl(Ext.XTemplate.getTpl(me, 'groupRowTpl')).groupingFeature = me; - view.preserveScrollOnRefresh = true; - // Sparse store - we can never collapse groups - if (store.isBufferedStore) { - me.collapsible = false; - } else // If it's a local store we can build a grouped store for use as the view's dataSource - { - // Share the GroupStore between both sides of a locked grid - lockPartner = me.lockingPartner; - if (lockPartner && lockPartner.dataSource) { - me.dataSource = view.dataSource = dataSource = lockPartner.dataSource; - } else { - me.dataSource = view.dataSource = dataSource = new Ext.grid.feature.GroupStore(me, store); - } - } - grid = grid.ownerLockable || grid; - // Before the reconfigure, rebind our GroupStore dataSource to the new store - grid.on('beforereconfigure', me.beforeReconfigure, me); - view.on({ - afterrender: me.afterViewRender, - scope: me, - single: true - }); - if (dataSource) { - // Listen to dataSource groupchange so it has a chance to do any processing - // before we react to it - dataSource.on('groupchange', me.onGroupChange, me); - } else { - me.setupStoreListeners(store); - } - me.mixins.summary.bindStore.call(me, grid, grid.getStore()); - }, - getGridStore: function() { - return this.view.getStore(); - }, - indexOf: function(record) { - return this.dataSource.indexOf(record); - }, - indexOfPlaceholder: function(record) { - return this.dataSource.indexOfPlaceholder(record); - }, - isInCollapsedGroup: function(record) { - var me = this, - store = me.getGridStore(), - result = false, - metaGroup; - if (store.isGrouped() && (metaGroup = me.getMetaGroup(record))) { - result = !!(metaGroup && metaGroup.isCollapsed); - } - return result; - }, - createCache: function() { - var metaGroupCache = this.metaGroupCache = {}, - lockingPartner = this.lockingPartner; - if (lockingPartner) { - lockingPartner.metaGroupCache = metaGroupCache; - } - metaGroupCache.map = {}; - return metaGroupCache; - }, - getCache: function() { - return this.metaGroupCache || this.createCache(); - }, - invalidateCache: function() { - var lockingPartner = this.lockingPartner; - this.metaGroupCache = null; - if (lockingPartner) { - lockingPartner.metaGroupCache = null; - } - }, - vetoEvent: function(record, row, rowIndex, e) { - // Do not veto mouseover/mouseout - if (e.type !== 'mouseover' && e.type !== 'mouseout' && e.type !== 'mouseenter' && e.type !== 'mouseleave' && e.getTarget(this.eventSelector)) { - return false; - } - }, - enable: function() { - var me = this, - view = me.view, - store = me.getGridStore(), - currentGroupedHeader = me.hideGroupedHeader && me.getGroupedHeader(), - groupToggleMenuItem; - view.isGrouping = true; - if (view.lockingPartner) { - view.lockingPartner.isGrouping = true; - } - me.callParent(); - if (me.lastGrouper) { - store.group(me.lastGrouper); - me.lastGrouper = null; - } - // Update the UI. - if (currentGroupedHeader) { - currentGroupedHeader.hide(); - } - groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem'); - if (groupToggleMenuItem) { - groupToggleMenuItem.setChecked(true, true); - } - }, - disable: function() { - var me = this, - view = me.view, - store = me.getGridStore(), - currentGroupedHeader = me.hideGroupedHeader && me.getGroupedHeader(), - lastGrouper = store.getGrouper(), - groupToggleMenuItem; - view.isGrouping = false; - if (view.lockingPartner) { - view.lockingPartner.isGrouping = false; - } - me.callParent(); - if (lastGrouper) { - me.lastGrouper = lastGrouper; - store.clearGrouping(); - } - // Update the UI. - if (currentGroupedHeader) { - currentGroupedHeader.show(); - } - groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem'); - if (groupToggleMenuItem) { - groupToggleMenuItem.setChecked(false, true); - groupToggleMenuItem.disable(); - } - }, - // Attach events to view - afterViewRender: function() { - var me = this, - view = me.view; - view.on({ - scope: me, - groupclick: me.onGroupClick - }); - if (me.enableGroupingMenu) { - me.injectGroupingMenu(); - } - me.pruneGroupedHeader(); - me.lastGrouper = me.getGridStore().getGrouper(); - // If disabled in the config, disable now so the store load won't - // send the grouping query params in the request. - if (me.disabled) { - me.disable(); - } - }, - injectGroupingMenu: function() { - var me = this, - headerCt = me.view.headerCt; - headerCt.showMenuBy = me.showMenuBy; - headerCt.getMenuItems = me.getMenuItems(); - }, - onColumnHideShow: function(headerOwnerCt, header) { - var me = this, - view = me.view, - headerCt = view.headerCt, - menu = headerCt.getMenu(), - activeHeader = menu.activeHeader, - groupMenuItem = menu.down('#groupMenuItem'), - groupMenuMeth, - colCount = me.grid.getVisibleColumnManager().getColumns().length, - items, len, i; - // "Group by this field" must be disabled if there's only one column left visible. - if (activeHeader && groupMenuItem) { - groupMenuMeth = activeHeader.groupable === false || !activeHeader.dataIndex || me.view.headerCt.getVisibleGridColumns().length < 2 ? 'disable' : 'enable'; - groupMenuItem[groupMenuMeth](); - } - // header containing TDs have to span all columns, hiddens are just zero width - // Also check the colCount on the off chance that they are all hidden - if (view.rendered && colCount) { - items = view.el.query('.' + me.ctCls); - for (i = 0 , len = items.length; i < len; ++i) { - items[i].colSpan = colCount; - } - } - }, - // Update first and last records in groups when column moves - // Because of the RowWrap template, this will update the groups' headers and footers - onColumnMove: function() { - var me = this, - view = me.view, - groupName, groupNames, group, firstRec, lastRec, metaGroup; - if (view.getStore().isGrouped()) { - groupNames = me.getCache().map; - Ext.suspendLayouts(); - for (groupName in groupNames) { - group = me.getGroup(groupName); - firstRec = group.first(); - lastRec = group.last(); - metaGroup = me.getMetaGroup(groupName); - if (metaGroup.isCollapsed) { - firstRec = lastRec = me.dataSource.getGroupPlaceholder(groupName); - } - view.refreshNode(firstRec); - if (me.showSummaryRow && lastRec !== firstRec) { - view.refreshNode(lastRec); - } - } - Ext.resumeLayouts(true); - } - }, - showMenuBy: function(clickEvent, t, header) { - var me = this, - menu = me.getMenu(), - groupMenuItem = menu.down('#groupMenuItem'), - groupMenuMeth = header.groupable === false || !header.dataIndex || me.view.headerCt.getVisibleGridColumns().length < 2 ? 'disable' : 'enable', - groupToggleMenuItem = menu.down('#groupToggleMenuItem'), - isGrouped = me.grid.getStore().isGrouped(); - groupMenuItem[groupMenuMeth](); - if (groupToggleMenuItem) { - groupToggleMenuItem.setChecked(isGrouped, true); - groupToggleMenuItem[isGrouped ? 'enable' : 'disable'](); - } - Ext.grid.header.Container.prototype.showMenuBy.apply(me, arguments); - }, - getMenuItems: function() { - var me = this, - groupByText = me.groupByText, - disabled = me.disabled || !me.getGroupField(), - showGroupsText = me.showGroupsText, - enableNoGroups = me.enableNoGroups, - getMenuItems = me.view.headerCt.getMenuItems; - // runs in the scope of headerCt - return function() { - // We cannot use the method from HeaderContainer's prototype here - // because other plugins or features may already have injected an implementation - var o = getMenuItems.call(this); - o.push('-', { - iconCls: Ext.baseCSSPrefix + 'group-by-icon', - itemId: 'groupMenuItem', - text: groupByText, - handler: me.onGroupMenuItemClick, - scope: me - }); - if (enableNoGroups) { - o.push({ - itemId: 'groupToggleMenuItem', - text: showGroupsText, - checked: !disabled, - checkHandler: me.onGroupToggleMenuItemClick, - scope: me - }); - } - return o; - }; - }, - /** - * Group by the header the user has clicked on. - * @private - */ - onGroupMenuItemClick: function(menuItem, e) { - var me = this, - menu = menuItem.parentMenu, - hdr = menu.activeHeader, - view = me.view, - store = me.getGridStore(); - if (me.disabled) { - me.lastGrouper = null; - me.block(); - me.enable(); - me.unblock(); - } - view.isGrouping = true; - // First check if there is a grouper defined for the feature. This is necessary - // when the value is a complex type. - store.group(me.getGrouper(hdr.dataIndex) || hdr.dataIndex); - me.pruneGroupedHeader(); - }, - block: function(fromPartner) { - var me = this; - me.blockRefresh = me.view.blockRefresh = true; - if (me.lockingPartner && !fromPartner) { - me.lockingPartner.block(true); - } - }, - unblock: function(fromPartner) { - var me = this; - me.blockRefresh = me.view.blockRefresh = false; - if (me.lockingPartner && !fromPartner) { - me.lockingPartner.unblock(true); - } - }, - /** - * Turn on and off grouping via the menu - * @private - */ - onGroupToggleMenuItemClick: function(menuItem, checked) { - this[checked ? 'enable' : 'disable'](); - }, - /** - * Prunes the grouped header from the header container - * @private - */ - pruneGroupedHeader: function() { - var me = this, - header = me.getGroupedHeader(); - if (me.hideGroupedHeader && header) { - Ext.suspendLayouts(); - if (me.prunedHeader && me.prunedHeader !== header) { - me.prunedHeader.show(); - } - me.prunedHeader = header; - if (header.rendered) { - header.hide(); - } - Ext.resumeLayouts(true); - } - }, - getHeaderNode: function(groupName) { - var el = this.view.getEl(), - nodes, i, len, node; - if (el) { - groupName = Ext.htmlEncode(groupName); - nodes = el.query(this.eventSelector); - for (i = 0 , len = nodes.length; i < len; ++i) { - node = nodes[i]; - if (node.getAttribute('data-groupName') === groupName) { - return node; - } - } - } - }, - getGroup: function(name) { - var store = this.getGridStore(), - value = name, - group; - if (store.isGrouped()) { - if (name.isModel) { - name = name.get(store.getGroupField()); - } - // If a complex type let's try to get the string from a groupFn. - if (typeof name !== 'string') { - name = store.getGrouper().getGroupString(value); - } - group = store.getGroups().getByKey(name); - } - return group; - }, - // Groupers may be defined on the feature itself if the datIndex is a complex type. - /** - * @private - * - */ - getGrouper: function(dataIndex) { - var groupers = this.groupers; - if (!groupers) { - return null; - } - return Ext.Array.findBy(groupers, function(grouper) { - return grouper.property === dataIndex; - }); - }, - getGroupField: function() { - return this.getGridStore().getGroupField(); - }, - getMetaGroup: function(group) { - var metaGroupCache = this.metaGroupCache || this.createCache(), - key, metaGroup; - if (group.isModel) { - group = this.getGroup(group); - } - // An empty string is a valid groupKey so only filter null and undefined. - if (group != null) { - key = (typeof group === 'string') ? group : group.getGroupKey(); - metaGroup = metaGroupCache[key]; - if (!metaGroup) { - // TODO: Break this out into its own method? - metaGroup = metaGroupCache[key] = { - isCollapsed: false, - lastGroup: null, - lastGroupGeneration: null, - lastFilterGeneration: null, - aggregateRecord: new Ext.data.Model() - }; - metaGroupCache.map[key] = true; - } - } - return metaGroup; - }, - /** - * Returns `true` if the named group is expanded. - * @param {String} groupName The group name. This is the value of - * the {@link Ext.data.Store#groupField groupField}. - * @return {Boolean} `true` if the group defined by that value is expanded. - */ - isExpanded: function(groupName) { - return !this.getMetaGroup(groupName).isCollapsed; - }, - /** - * Expand a group - * @param {String} groupName The group name - * @param {Boolean} focus Pass `true` to focus the group after expand. - */ - expand: function(groupName, focus) { - this.doCollapseExpand(false, groupName, focus); - }, - /** - * Expand all groups - */ - expandAll: function() { - var me = this, - metaGroupCache = me.getCache(), - lockingPartner = me.lockingPartner, - groupName; - // Clear all collapsed flags. - // metaGroupCache is shared between two lockingPartners - for (groupName in metaGroupCache) { - if (metaGroupCache.hasOwnProperty(groupName)) { - metaGroupCache[groupName].isCollapsed = false; - } - } - // We do not need to inform our lockingPartner. - // It shares the same group cache - it will have the same set of expanded groups. - Ext.suspendLayouts(); - me.dataSource.onRefresh(); - Ext.resumeLayouts(true); - // Fire event for all groups post expand - for (groupName in metaGroupCache) { - if (metaGroupCache.hasOwnProperty(groupName)) { - me.afterCollapseExpand(false, groupName); - if (lockingPartner) { - lockingPartner.afterCollapseExpand(false, groupName); - } - } - } - }, - /** - * Collapse a group - * @param {String} groupName The group name - * @param {Boolean} focus Pass `true` to focus the group after expand. - */ - collapse: function(groupName, focus) { - this.doCollapseExpand(true, groupName, focus); - }, - /** - * @private - * Returns true if all groups are collapsed - * @return {boolean} - */ - isAllCollapsed: function() { - var me = this, - metaGroupCache = me.getCache(), - groupName; - // Clear all collapsed flags. - // metaGroupCache is shared between two lockingPartners - for (groupName in metaGroupCache) { - if (metaGroupCache.hasOwnProperty(groupName)) { - if (!metaGroupCache[groupName].isCollapsed) { - return false; - } - } - } - return true; - }, - /** - * @private - * Returns true if all groups are expanded - * @return {boolean} - */ - isAllExpanded: function() { - var me = this, - metaGroupCache = me.getCache(), - groupName; - // Clear all collapsed flags. - // metaGroupCache is shared between two lockingPartners - for (groupName in metaGroupCache) { - if (metaGroupCache.hasOwnProperty(groupName)) { - if (metaGroupCache[groupName].isCollapsed) { - return false; - } - } - } - return true; - }, - /** - * Collapse all groups - */ - collapseAll: function() { - var me = this, - metaGroupCache = me.getCache(), - groupName, - lockingPartner = me.lockingPartner; - // Set all collapsed flags - // metaGroupCache is shared between two lockingPartners - for (groupName in metaGroupCache) { - if (metaGroupCache.hasOwnProperty(groupName)) { - metaGroupCache[groupName].isCollapsed = true; - } - } - // We do not need to inform our lockingPartner. - // It shares the same group cache - it will have the same set of collapsed groups. - Ext.suspendLayouts(); - me.dataSource.onRefresh(); - Ext.resumeLayouts(true); - // Fire event for all groups post collapse - for (groupName in metaGroupCache) { - if (metaGroupCache.hasOwnProperty(groupName)) { - me.afterCollapseExpand(true, groupName); - if (lockingPartner) { - lockingPartner.afterCollapseExpand(true, groupName); - } - } - } - }, - doCollapseExpand: function(collapsed, groupName, focus) { - var me = this, - lockingPartner = me.lockingPartner, - group = me.getGroup(groupName); - // metaGroupCache is shared between two lockingPartners. - if (me.getMetaGroup(group).isCollapsed !== collapsed) { - me.isExpandingOrCollapsing = true; - // The GroupStore is shared by partnered Grouping features, so this will refresh both sides. - // We only want one layout as a result though, so suspend layouts while refreshing. - Ext.suspendLayouts(); - if (collapsed) { - me.dataSource.collapseGroup(group); - } else { - me.dataSource.expandGroup(group); - } - Ext.resumeLayouts(true); - // Sync the group state and focus the row if requested. - me.afterCollapseExpand(collapsed, groupName, focus); - // Sync the lockingPartner's group state. - // Do not pass on focus flag. If we were told to focus, we must focus, not the other side. - if (lockingPartner) { - lockingPartner.afterCollapseExpand(collapsed, groupName, false); - } - me.isExpandingOrCollapsing = false; - } - }, - afterCollapseExpand: function(collapsed, groupName, focus) { - var me = this, - view = me.view, - bufferedRenderer = view.bufferedRenderer, - header; - header = me.getHeaderNode(groupName); - view.fireEvent(collapsed ? 'groupcollapse' : 'groupexpand', view, header, groupName); - if (focus) { - if (header) { - view.scrollElIntoView(Ext.fly(header).up(view.getItemSelector()), false, true); - } - // The header might be outside the rendered range if we are buffer rendering - else if (bufferedRenderer) { - // Find the first record in the group and ask the buffered renderer to take us there - bufferedRenderer.scrollTo(me.getGroup(groupName).getAt(0)); - } - } - }, - onGroupChange: function(store, grouper) { - // If changed to a non-null grouper, the Store will be sorted (either remotely or locally), and therefore fire a refresh. - // If changed to a null grouper - setGrouper(null) - that causes no mutation to a store, so we must refresh the view to remove the group headers/footers. - if (!grouper) { - this.view.ownerGrid.getView().refreshView(); - } else { - this.lastGrouper = grouper; - } - }, - /** - * Gets the related menu item for a dataIndex - * @private - * @return {Ext.grid.header.Container} The header - */ - getMenuItem: function(dataIndex) { - var view = this.view, - header = view.headerCt.down('gridcolumn[dataIndex=' + dataIndex + ']'), - menu = view.headerCt.getMenu(); - return header ? menu.down('menuitem[headerId=' + header.id + ']') : null; - }, - onGroupKey: function(keyCode, event) { - var me = this, - groupName = me.getGroupName(event.target); - if (groupName) { - me.onGroupClick(me.view, event.target, groupName, event); - } - }, - /** - * Toggle between expanded/collapsed state when clicking on - * the group. - * @private - */ - onGroupClick: function(view, rowElement, groupName, e) { - var me = this, - metaGroupCache = me.getCache(), - groupNames = metaGroupCache.map, - groupIsCollapsed = !me.isExpanded(groupName), - g; - if (me.collapsible) { - // CTRL means collapse all others. - if (e.ctrlKey) { - Ext.suspendLayouts(); - for (g in groupNames) { - if (g === groupName) { - if (groupIsCollapsed) { - me.expand(groupName); - } - } else if (!metaGroupCache[g].isCollapsed) { - me.doCollapseExpand(true, g, false); - } - } - Ext.resumeLayouts(true); - return; - } - if (groupIsCollapsed) { - me.expand(groupName); - } else { - me.collapse(groupName); - } - } - }, - setupRowData: function(record, idx, rowValues) { - var me = this, - recordIndex = rowValues.recordIndex, - data = me.refreshData, - metaGroupCache = me.getCache(), - header = data.header, - groupField = data.groupField, - store = me.getGridStore(), - dataSource = me.view.dataSource, - isBufferedStore = dataSource.isBufferedStore, - column = me.grid.columnManager.getHeaderByDataIndex(groupField), - hasRenderer = !!(column && column.renderer), - groupKey = record.groupKey, - // MetaGroup placheholder records store the groupKey not a reference. - // See EXTJS-18655. - group = record.isCollapsedPlaceholder && groupKey ? me.getGroup(groupKey) : record.group, - grouper, groupName, prev, next, items; - rowValues.isCollapsedGroup = false; - rowValues.summaryRecord = rowValues.groupHeaderCls = null; - if (data.doGrouping) { - grouper = store.getGrouper(); - // This is a placeholder record which represents a whole collapsed group - // It is a special case. - if (record.isCollapsedPlaceholder) { - groupName = group.getGroupKey(); - items = group.items; - rowValues.isFirstRow = rowValues.isLastRow = true; - rowValues.groupHeaderCls = me.hdCollapsedCls; - rowValues.isCollapsedGroup = rowValues.needsWrap = true; - rowValues.groupName = groupName; - rowValues.metaGroupCache = metaGroupCache; - metaGroupCache.groupField = groupField; - metaGroupCache.name = metaGroupCache.renderedGroupValue = hasRenderer ? column.renderer(group.getAt(0).get(groupField), {}, record) : groupName; - metaGroupCache.groupValue = items[0].get(groupField); - metaGroupCache.columnName = header ? header.text : groupField; - rowValues.collapsibleCls = me.collapsible ? me.collapsibleCls : me.hdNotCollapsibleCls; - metaGroupCache.rows = metaGroupCache.children = items; - if (me.showSummaryRow) { - rowValues.summaryRecord = data.summaryData[groupName]; - } - return; - } - groupName = grouper.getGroupString(record); - // If caused by an update event on the first or last records of a group fired by a GroupStore, the record's group will be attached. - if (group) { - items = group.items; - rowValues.isFirstRow = record === items[0]; - rowValues.isLastRow = record === items[items.length - 1]; - } else { - // See if the current record is the last in the group - rowValues.isFirstRow = recordIndex === 0; - if (!rowValues.isFirstRow) { - prev = store.getAt(recordIndex - 1); - // If the previous row is of a different group, then we're at the first for a new group - if (prev) { - // Must use Model's comparison because Date objects are never equal - rowValues.isFirstRow = !prev.isEqual(grouper.getGroupString(prev), groupName); - } - } - // See if the current record is the last in the group - rowValues.isLastRow = recordIndex === (isBufferedStore ? store.getTotalCount() : store.getCount()) - 1; - if (!rowValues.isLastRow) { - next = store.getAt(recordIndex + 1); - if (next) { - // Must use Model's comparison because Date objects are never equal - rowValues.isLastRow = !next.isEqual(grouper.getGroupString(next), groupName); - } - } - } - if (rowValues.isFirstRow) { - metaGroupCache.groupField = groupField; - metaGroupCache.name = metaGroupCache.renderedGroupValue = hasRenderer ? column.renderer(record.get(groupField), {}, record) : groupName; - metaGroupCache.groupValue = record.get(groupField); - metaGroupCache.columnName = header ? header.text : groupField; - rowValues.collapsibleCls = me.collapsible ? me.collapsibleCls : me.hdNotCollapsibleCls; - rowValues.groupName = groupName; - if (!me.isExpanded(groupName)) { - rowValues.itemClasses.push(me.hdCollapsedCls); - rowValues.isCollapsedGroup = true; - } - // We only get passed a GroupStore if the store is not buffered. - if (isBufferedStore) { - metaGroupCache.rows = metaGroupCache.children = []; - } else { - metaGroupCache.rows = metaGroupCache.children = me.getRecordGroup(record).items; - } - rowValues.metaGroupCache = metaGroupCache; - } - if (rowValues.isLastRow) { - // Add the group's summary record to the last record in the group - if (me.showSummaryRow) { - rowValues.summaryRecord = data.summaryData[groupName]; - rowValues.itemClasses.push(Ext.baseCSSPrefix + 'grid-group-last'); - } - } - rowValues.needsWrap = (rowValues.isFirstRow || rowValues.summaryRecord); - } - }, - setup: function(rows, rowValues) { - var me = this, - data = me.refreshData, - view = rowValues.view, - // Need to check if groups have been added since init(), such as in the case of stateful grids. - isGrouping = view.isGrouping = !me.disabled && me.getGridStore().isGrouped(), - bufferedRenderer = view.bufferedRenderer; - me.skippedRows = 0; - if (bufferedRenderer) { - bufferedRenderer.variableRowHeight = view.bufferedRenderer.variableRowHeight || isGrouping; - } - data.groupField = me.getGroupField(); - data.header = me.getGroupedHeader(data.groupField); - data.doGrouping = isGrouping; - rowValues.groupHeaderTpl = Ext.XTemplate.getTpl(me, 'groupHeaderTpl'); - if (isGrouping && me.showSummaryRow) { - data.summaryData = me.generateSummaryData(); - } - }, - cleanup: function(rows, rowValues) { - var data = this.refreshData; - rowValues.metaGroupCache = rowValues.groupHeaderTpl = rowValues.isFirstRow = null; - data.groupField = data.header = data.summaryData = null; - }, - getAggregateRecord: function(metaGroup, forceNew) { - var rec; - if (forceNew === true || !metaGroup.aggregateRecord) { - rec = new Ext.data.Model(); - metaGroup.aggregateRecord = rec; - rec.isNonData = rec.isSummary = true; - } - return metaGroup.aggregateRecord; - }, - /** - * Used by the Grouping Feature when {@link #showSummaryRow} is `true`. - * - * Generates group summary data for the whole store. - * @private - * @return {Object} An object hash keyed by group name containing summary records. - */ - generateSummaryData: function() { - var me = this, - store = me.getGridStore(), - filters = store.getFilters(), - groups = store.getGroups().items, - reader = store.getProxy().getReader(), - groupField = me.getGroupField(), - lockingPartner = me.lockingPartner, - updateSummaryRow = me.updateSummaryRow, - data = {}, - ownerCt = me.view.ownerCt, - i, len, group, metaGroup, record, hasRemote, remoteData; - /** - * @cfg {String} [remoteRoot=undefined] - * The name of the property which contains the Array of summary objects. - * It allows to use server-side calculated summaries. - */ - if (me.remoteRoot) { - remoteData = me.mixins.summary.generateSummaryData.call(me, groupField); - hasRemote = !!remoteData; - } - for (i = 0 , len = groups.length; i < len; ++i) { - group = groups[i]; - metaGroup = me.getMetaGroup(group); - // Something has changed or it doesn't exist, populate it. - if (updateSummaryRow || hasRemote || store.updating || me.grid.reconfiguring || me.didGroupChange(group, metaGroup, filters)) { - record = me.populateRecord(group, metaGroup, remoteData); - // Clear the dirty state of the group if this is the only Summary, or this is the right hand (normal grid's) summary. - if (!lockingPartner || (ownerCt === ownerCt.ownerLockable.normalGrid)) { - metaGroup.lastGroup = group; - metaGroup.lastGroupGeneration = group.generation; - metaGroup.lastFilterGeneration = filters.generation; - } - } else { - record = me.getAggregateRecord(metaGroup); - } - data[group.getGroupKey()] = record; - } - me.updateSummaryRow = false; - return data; - }, - getGroupName: function(element) { - var me = this, - view = me.view, - eventSelector = me.eventSelector, - targetEl, row; - // See if element is, or is within a group header. If so, we can extract its name - targetEl = Ext.fly(element).findParent(eventSelector); - if (!targetEl) { - // Otherwise, navigate up to the row and look down to see if we can find it - row = Ext.fly(element).findParent(view.itemSelector); - if (row) { - targetEl = row.down(eventSelector, true); - } - } - if (targetEl) { - return Ext.htmlDecode(targetEl.getAttribute('data-groupname')); - } - }, - /** - * Returns the group data object for the group to which the passed record belongs **if the Store is grouped**. - * - * @param {Ext.data.Model} record The record for which to return group information. - * @return {Object} A single group data block as returned from {@link Ext.data.Store#getGroups Store.getGroups}. Returns - * `undefined` if the Store is not grouped. - * - */ - getRecordGroup: function(record) { - var store = this.getGridStore(), - grouper = store.getGrouper(); - if (grouper) { - return store.getGroups().getByKey(grouper.getGroupString(record)); - } - }, - getGroupedHeader: function(groupField) { - var me = this, - headerCt = me.view.headerCt, - partner = me.lockingPartner, - selector, header; - groupField = groupField || me.getGroupField(); - if (groupField) { - selector = '[dataIndex=' + groupField + ']'; - header = headerCt.down(selector); - // The header may exist in the locking partner, so check there as well - if (!header && partner) { - header = partner.view.headerCt.down(selector); - } - } - return header || null; - }, - getFireEventArgs: function(type, view, targetEl, e) { - return [ - type, - view, - targetEl, - this.getGroupName(targetEl), - e - ]; - }, - destroy: function() { - var me = this, - dataSource = me.dataSource; - me.storeListeners = Ext.destroy(me.storeListeners); - me.view = me.prunedHeader = me.grid = me.dataSource = me.groupers = null; - me.invalidateCache(); - me.callParent(); - if (dataSource) { - dataSource.bindStore(null); - Ext.destroy(dataSource); - } - }, - beforeReconfigure: function(grid, store, columns, oldStore, oldColumns) { - var me = this, - view = me.view, - dataSource = me.dataSource, - bufferedStore; - if (store && store !== oldStore) { - bufferedStore = store.isBufferedStore; - if (!dataSource) { - Ext.destroy(me.storeListeners); - me.setupStoreListeners(store); - } - // Grouping involves injecting a dataSource in early - if (bufferedStore !== oldStore.isBufferedStore) { - Ext.raise('Cannot reconfigure grouping switching between buffered and non-buffered stores'); - } - view.isGrouping = !!store.getGrouper(); - dataSource.bindStore(store); - } - }, - populateRecord: function(group, metaGroup, remoteData) { - var me = this, - view = me.grid.ownerLockable ? me.grid.ownerLockable.view : me.view, - store = me.getGridStore(), - record = me.getAggregateRecord(metaGroup), - // Use the full column set, regardless of locking - columns = view.headerCt.getGridColumns(), - len = columns.length, - groupName = group.getGroupKey(), - groupData, field, i, column, fieldName, summaryValue; - record.beginEdit(); - if (remoteData) { - // Remote summary grouping provides the grouping totals so there's no need to - // iterate throught the columns to map the column's dataIndex to the field name. - // Instead, enumerate the grouping record and set the field in the aggregate - // record for each one. - groupData = remoteData[groupName]; - for (field in groupData) { - if (groupData.hasOwnProperty(field)) { - if (field !== record.idProperty) { - record.set(field, groupData[field]); - } - } - } - } - // Here we iterate through the columns with two objectives: - // 1. For local grouping, get the summary for each column and update the record. - // 2. For both local and remote grouping, set the summary data object - // which is passed to the summaryRenderer (if defined). - for (i = 0; i < len; ++i) { - column = columns[i]; - // Use the column id if there's no mapping, could be a calculated field - fieldName = column.dataIndex || column.getItemId(); - // We need to capture the summary value because it could get overwritten when - // setting on the model if there is a convert() method on the model. - if (!remoteData) { - summaryValue = me.getSummary(store, column.summaryType, fieldName, group); - record.set(fieldName, summaryValue); - } else { - // For remote groupings, just get the value from the model. - summaryValue = record.get(column.dataIndex); - } - // Capture the columnId:value for the summaryRenderer in the summaryData object. - me.setSummaryData(record, column.getItemId(), summaryValue, groupName); - } - // Poke on the owner group for easy lookup in this.createRenderer(). - record.ownerGroup = groupName; - record.endEdit(true); - record.commit(); - return record; - }, - privates: { - didGroupChange: function(group, metaGroup, filters) { - var ret = true; - if (group === metaGroup.lastGroup) { - ret = metaGroup.lastGroupGeneration !== group.generation || metaGroup.lastFilterGeneration !== filters.generation; - } - return ret; - }, - setupStoreListeners: function(store) { - var me = this; - me.storeListeners = store.on({ - groupchange: me.onGroupChange, - scope: me, - destroyable: true - }); - } - } -}); - -/** - * This feature adds an aggregate summary row at the bottom of each group that is provided - * by the {@link Ext.grid.feature.Grouping} feature. There are two aspects to the summary: - * - * ## Calculation - * - * The summary value needs to be calculated for each column in the grid. This is controlled - * by the summaryType option specified on the column. There are several built in summary types, - * which can be specified as a string on the column configuration. These call underlying methods - * on the store: - * - * - {@link Ext.data.Store#count count} - * - {@link Ext.data.Store#sum sum} - * - {@link Ext.data.Store#min min} - * - {@link Ext.data.Store#max max} - * - {@link Ext.data.Store#average average} - * - * Alternatively, the summaryType can be a function definition. If this is the case, - * the function is called with two parameters: an array of records, and an array of field values - * to calculate the summary value. - * - * ## Rendering - * - * Similar to a column, the summary also supports a summaryRenderer function. This - * summaryRenderer is called before displaying a value. The function is optional, if - * not specified the default calculated value is shown. The summaryRenderer is called with: - * - * - value {Object} - The calculated value. - * - summaryData {Object} - Contains all raw summary values for the row. - * - field {String} - The name of the field we are calculating - * - metaData {Object} - A collection of metadata about the current cell; can be used or modified by the renderer. - * - * ## Example Usage - * - * @example - * Ext.define('TestResult', { - * extend: 'Ext.data.Model', - * fields: ['student', 'subject', { - * name: 'mark', - * type: 'int' - * }] - * }); - * - * Ext.create('Ext.grid.Panel', { - * width: 200, - * height: 240, - * renderTo: document.body, - * features: [{ - * groupHeaderTpl: 'Subject: {name}', - * ftype: 'groupingsummary' - * }], - * store: { - * model: 'TestResult', - * groupField: 'subject', - * data: [{ - * student: 'Student 1', - * subject: 'Math', - * mark: 84 - * },{ - * student: 'Student 1', - * subject: 'Science', - * mark: 72 - * },{ - * student: 'Student 2', - * subject: 'Math', - * mark: 96 - * },{ - * student: 'Student 2', - * subject: 'Science', - * mark: 68 - * }] - * }, - * columns: [{ - * dataIndex: 'student', - * text: 'Name', - * summaryType: 'count', - * summaryRenderer: function(value){ - * return Ext.String.format('{0} student{1}', value, value !== 1 ? 's' : ''); - * } - * }, { - * dataIndex: 'mark', - * text: 'Mark', - * summaryType: 'average' - * }] - * }); - */ -Ext.define('Ext.grid.feature.GroupingSummary', { - extend: 'Ext.grid.feature.Grouping', - alias: 'feature.groupingsummary', - showSummaryRow: true, - vetoEvent: function(record, row, rowIndex, e) { - var result = this.callParent(arguments); - if (result !== false && e.getTarget(this.summaryRowSelector)) { - result = false; - } - return result; - } -}); - -/** - * The rowbody feature enhances the grid's markup to have an additional - * tr -> td -> div which spans the entire width of the original row. - * - * This is useful to to associate additional information with a particular - * record in an Ext.grid.Grid. - * - * Rowbodies are initially hidden unless you override {@link #getAdditionalData}. - * - * The events fired by RowBody are relayed to the owning - * {@link Ext.view.Table grid view} (and subsequently the owning grid). - * - * # Example - * - * @example - * Ext.define('Animal', { - * extend: 'Ext.data.Model', - * fields: ['name', 'latin', 'desc', 'lifespan'] - * }); - * - * Ext.create('Ext.grid.Panel', { - * width: 400, - * height: 300, - * renderTo: Ext.getBody(), - * store: { - * model: 'Animal', - * data: [{ - * name: 'Tiger', - * latin: 'Panthera tigris', - * desc: 'The largest cat species, weighing up to 306 kg (670 lb).', - * lifespan: '20 - 26 years (in captivity)' - * }, { - * name: 'Roman snail', - * latin: 'Helix pomatia', - * desc: 'A species of large, edible, air-breathing land snail.', - * lifespan: '20 - 35 years' - * }, { - * name: 'Yellow-winged darter', - * latin: 'Sympetrum flaveolum', - * desc: 'A dragonfly found in Europe and mid and Northern China.', - * lifespan: '4 - 6 weeks' - * }, { - * name: 'Superb Fairy-wren', - * latin: 'Malurus cyaneus', - * desc: 'Common and familiar across south-eastern Australia.', - * lifespan: '5 - 6 years' - * }] - * }, - * columns: [{ - * dataIndex: 'name', - * text: 'Common name', - * width: 125 - * }, { - * dataIndex: 'latin', - * text: 'Scientific name', - * flex: 1 - * }], - * features: [{ - * ftype: 'rowbody', - * getAdditionalData: function (data, idx, record, orig) { - * // Usually you would style the my-body-class in a CSS file - * return { - * rowBody: '
    ' + record.get("desc") + '
    ', - * rowBodyCls: "my-body-class" - * }; - * } - * }], - * listeners: { - * rowbodyclick: function(view, rowEl, e, eOpts) { - * var itemEl = Ext.get(rowEl).up(view.itemSelector), - * rec = view.getRecord(itemEl); - * - * Ext.Msg.alert(rec.get('name') + ' life span', rec.get('lifespan')); - * } - * } - * }); - * - * # Cell Editing and Cell Selection Model - * - * Note that if {@link Ext.grid.plugin.CellEditing cell editing} or the {@link Ext.selection.CellModel cell selection model} are going - * to be used, then the {@link Ext.grid.feature.RowBody RowBody} feature, or {@link Ext.grid.plugin.RowExpander RowExpander} plugin MUST - * be used for intra-cell navigation to be correct. - */ -Ext.define('Ext.grid.feature.RowBody', { - extend: 'Ext.grid.feature.Feature', - alias: 'feature.rowbody', - rowBodyCls: Ext.baseCSSPrefix + 'grid-row-body', - rowBodyHiddenCls: Ext.baseCSSPrefix + 'grid-row-body-hidden', - rowBodyTdSelector: 'td.' + Ext.baseCSSPrefix + 'grid-cell-rowbody', - eventPrefix: 'rowbody', - eventSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-rowbody-tr', - colSpanDecrement: 0, - /** - * @cfg {Boolean} [bodyBefore=false] - * Configure as `true` to put the row expander body *before* the data row. - */ - bodyBefore: false, - outerTpl: { - fn: function(out, values, parent) { - var view = values.view, - rowValues = view.rowValues; - this.rowBody.setup(values.rows, rowValues); - this.nextTpl.applyOut(values, out, parent); - this.rowBody.cleanup(values.rows, rowValues); - }, - priority: 100 - }, - extraRowTpl: [ - '{%', - 'if(this.rowBody.bodyBefore) {', - // MUST output column sizing elements because the first row in this table - // contains one colspanning TD, and that overrides subsequent column width settings. - 'values.view.renderColumnSizer(values, out);', - '} else {', - 'this.nextTpl.applyOut(values, out, parent);', - '}', - 'values.view.rowBodyFeature.setupRowData(values.record, values.recordIndex, values);', - '%}', - '', - '', - '', - '{%', - 'if(this.rowBody.bodyBefore) {', - 'this.nextTpl.applyOut(values, out, parent);', - '}', - '%}', - { - priority: 100, - beginRowSync: function(rowSync) { - rowSync.add('rowBody', this.owner.eventSelector); - }, - syncContent: function(destRow, sourceRow, columnsToUpdate) { - var owner = this.owner, - destRowBody = Ext.fly(destRow).down(owner.eventSelector, true), - sourceRowBody; - // Sync the heights of row body elements in each row if they need it. - if (destRowBody && (sourceRowBody = Ext.fly(sourceRow).down(owner.eventSelector, true))) { - Ext.fly(destRowBody).syncContent(sourceRowBody); - } - } - } - ], - init: function(grid) { - var me = this, - view = me.view = grid.getView(); - // The extra data means variableRowHeight - grid.variableRowHeight = view.variableRowHeight = true; - view.rowBodyFeature = me; - grid.mon(view, { - element: 'el', - click: me.onClick, - scope: me - }); - view.headerCt.on({ - columnschanged: me.onColumnsChanged, - scope: me - }); - view.addTpl(me.outerTpl).rowBody = me; - view.addRowTpl(Ext.XTemplate.getTpl(this, 'extraRowTpl')).rowBody = me; - me.callParent(arguments); - }, - // Needed to select the data row when clicked on the body row. - onClick: function(e) { - var me = this, - tableRow = e.getTarget(me.eventSelector); - // If we have clicked on a row body TR and its previous (or next - we can put the body first) sibling is a grid row, - // pass that onto the view for processing - if (tableRow && Ext.fly(tableRow = (tableRow.previousSibling || tableRow.nextSibling)).is(me.view.rowSelector)) { - e.target = tableRow; - me.view.handleEvent(e); - } - }, - getSelectedRow: function(view, rowIndex) { - var selectedRow = view.getNode(rowIndex); - if (selectedRow) { - return Ext.fly(selectedRow).down(this.eventSelector); - } - return null; - }, - // When columns added/removed, keep row body colspan in sync with number of columns. - onColumnsChanged: function(headerCt) { - var items = this.view.el.query(this.rowBodyTdSelector), - colspan = headerCt.getVisibleGridColumns().length, - len = items.length, - i; - for (i = 0; i < len; ++i) { - items[i].setAttribute('colSpan', colspan); - } - }, - /** - * @method getAdditionalData - * @protected - * @template - * Provides additional data to the prepareData call within the grid view. - * The rowbody feature adds 3 additional variables into the grid view's template. - * These are `rowBody`, `rowBodyCls`, and `rowBodyColspan`. - * - * - **rowBody:** *{String}* The HTML to display in the row body element. Defaults - * to *undefined*. - * - **rowBodyCls:** *{String}* An optional CSS class (or multiple classes - * separated by spaces) to apply to the row body element. Defaults to - * {@link #rowBodyCls}. - * - **rowBodyColspan:** *{Number}* The number of columns that the row body element - * should span. Defaults to the number of visible columns. - * - * @param {Object} data The data for this particular record. - * @param {Number} idx The row index for this record. - * @param {Ext.data.Model} record The record instance - * @param {Object} orig The original result from the prepareData call to massage. - * @return {Object} An object containing additional variables for use in the grid - * view's template - */ - /* - * @private - */ - setupRowData: function(record, rowIndex, rowValues) { - if (this.getAdditionalData) { - Ext.apply(rowValues, this.getAdditionalData(record.data, rowIndex, record, rowValues)); - } - }, - setup: function(rows, rowValues) { - rowValues.rowBodyCls = this.rowBodyCls; - rowValues.rowBodyColspan = this.view.headerCt.visibleColumnManager.getColumns().length - this.colSpanDecrement; - }, - cleanup: function(rows, rowValues) { - rowValues.rowBodyCls = rowValues.rowBodyColspan = rowValues.rowBody = null; - } -}); -/** - * @event beforerowbodymousedown - * @preventable - * @member Ext.view.Table - * Fires before the mousedown event on a row body element is processed. Return false - * to cancel the default action. - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @param {Ext.view.View} view The rowbody's owning View - * @param {HTMLElement} rowBodyEl The row body's element - * @param {Ext.event.Event} e The raw event object - */ -/** - * @event beforerowbodymouseup - * @preventable - * @member Ext.view.Table - * Fires before the mouseup event on a row body element is processed. Return false - * to cancel the default action. - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ -/** - * @event beforerowbodyclick - * @preventable - * @member Ext.view.Table - * Fires before the click event on a row body element is processed. Return false to - * cancel the default action. - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ -/** - * @event beforerowbodydblclick - * @preventable - * @member Ext.view.Table - * Fires before the dblclick event on a row body element is processed. Return false - * to cancel the default action. - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ -/** - * @event beforerowbodycontextmenu - * @preventable - * @member Ext.view.Table - * Fires before the contextmenu event on a row body element is processed. Return - * false to cancel the default action. - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ -/** - * @event beforerowbodylongpress - * @preventable - * @member Ext.view.Table - * Fires before the longpress event on a row body element is processed. Return - * false to cancel the default action. - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ -/** - * @event beforerowbodykeydown - * @preventable - * @member Ext.view.Table - * Fires before the keydown event on a row body element is processed. Return false - * to cancel the default action. - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ -/** - * @event beforerowbodykeyup - * @preventable - * @member Ext.view.Table - * Fires before the keyup event on a row body element is processed. Return false to - * cancel the default action. - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ -/** - * @event beforerowbodykeypress - * @preventable - * @member Ext.view.Table - * Fires before the keypress event on a row body element is processed. Return false - * to cancel the default action. - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ -/** - * @event rowbodymousedown - * @member Ext.view.Table - * Fires when there is a mouse down on a row body element - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ -/** - * @event rowbodymouseup - * @member Ext.view.Table - * Fires when there is a mouse up on a row body element - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ -/** - * @event rowbodyclick - * @member Ext.view.Table - * Fires when a row body element is clicked - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ -/** - * @event rowbodydblclick - * @member Ext.view.Table - * Fires when a row body element is double clicked - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ -/** - * @event rowbodycontextmenu - * @member Ext.view.Table - * Fires when a row body element is right clicked - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ -/** - * @event rowbodylongpress - * @member Ext.view.Table - * Fires on a row body element longpress event - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ -/** - * @event rowbodykeydown - * @member Ext.view.Table - * Fires when a key is pressed down while a row body element is currently selected - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ -/** - * @event rowbodykeyup - * @member Ext.view.Table - * Fires when a key is released while a row body element is currently selected - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ -/** - * @event rowbodykeypress - * @member Ext.view.Table - * Fires when a key is pressed while a row body element is currently selected. - * - * **Note:** This event is fired only when the Ext.grid.feature.RowBody feature is - * used. - * - * @inheritdoc #beforerowbodymousedown - */ - -/** - * This feature is used to place a summary row at the bottom of the grid. If using a grouping, - * see {@link Ext.grid.feature.GroupingSummary}. There are 2 aspects to calculating the summaries, - * calculation and rendering. - * - * ## Calculation - * The summary value needs to be calculated for each column in the grid. This is controlled - * by the summaryType option specified on the column. There are several built in summary types, - * which can be specified as a string on the column configuration. These call underlying methods - * on the store: - * - * - {@link Ext.data.Store#count count} - * - {@link Ext.data.Store#sum sum} - * - {@link Ext.data.Store#min min} - * - {@link Ext.data.Store#max max} - * - {@link Ext.data.Store#average average} - * - * Alternatively, the summaryType can be a function definition. If this is the case, - * the function is called with an array of records to calculate the summary value. - * - * ## Rendering - * Similar to a column, the summary also supports a summaryRenderer function. This - * summaryRenderer is called before displaying a value. The function is optional, if - * not specified the default calculated value is shown. The summaryRenderer is called with: - * - * - value {Object} - The calculated value. - * - summaryData {Object} - Contains all raw summary values for the row. - * - field {String} - The name of the field we are calculating - * - metaData {Object} - A collection of metadata about the current cell; can be used or modified by the renderer. - * - * ## Example Usage - * - * @example - * Ext.define('TestResult', { - * extend: 'Ext.data.Model', - * fields: ['student', { - * name: 'mark', - * type: 'int' - * }] - * }); - * - * Ext.create('Ext.grid.Panel', { - * width: 400, - * height: 200, - * title: 'Summary Test', - * style: 'padding: 20px', - * renderTo: document.body, - * features: [{ - * ftype: 'summary' - * }], - * store: { - * model: 'TestResult', - * data: [{ - * student: 'Student 1', - * mark: 84 - * },{ - * student: 'Student 2', - * mark: 72 - * },{ - * student: 'Student 3', - * mark: 96 - * },{ - * student: 'Student 4', - * mark: 68 - * }] - * }, - * columns: [{ - * dataIndex: 'student', - * text: 'Name', - * summaryType: 'count', - * summaryRenderer: function(value, summaryData, dataIndex) { - * return Ext.String.format('{0} student{1}', value, value !== 1 ? 's' : ''); - * } - * }, { - * dataIndex: 'mark', - * text: 'Mark', - * summaryType: 'average' - * }] - * }); - */ -Ext.define('Ext.grid.feature.Summary', { - /* Begin Definitions */ - extend: 'Ext.grid.feature.AbstractSummary', - alias: 'feature.summary', - /** - * @cfg {String} dock - * Configure `'top'` or `'bottom'` top create a fixed summary row either above or below the scrollable table. - * - */ - dock: undefined, - dockedSummaryCls: Ext.baseCSSPrefix + 'docked-summary', - panelBodyCls: Ext.baseCSSPrefix + 'summary-', - // turn off feature events. - hasFeatureEvent: false, - fullSummaryTpl: [ - '{%', - 'var me = this.summaryFeature,', - ' record = me.summaryRecord,', - ' view = values.view,', - ' bufferedRenderer = view.bufferedRenderer;', - 'this.nextTpl.applyOut(values, out, parent);', - 'if (!me.disabled && me.showSummaryRow && view.store.isLast(values.record)) {', - 'if (bufferedRenderer) {', - ' bufferedRenderer.variableRowHeight = true;', - '}', - 'me.outputSummaryRecord((record && record.isModel) ? record : me.createSummaryRecord(view), values, out, parent);', - '}', - '%}', - { - priority: 300, - beginRowSync: function(rowSync) { - rowSync.add('fullSummary', this.summaryFeature.summaryRowSelector); - }, - syncContent: function(destRow, sourceRow, columnsToUpdate) { - destRow = Ext.fly(destRow, 'syncDest'); - sourceRow = Ext.fly(sourceRow, 'sycSrc'); - var owner = this.owner, - selector = owner.summaryRowSelector, - destSummaryRow = destRow.down(selector, true), - sourceSummaryRow = sourceRow.down(selector, true); - // Sync just the updated columns in the summary row. - if (destSummaryRow && sourceSummaryRow) { - // If we were passed a column set, only update those, otherwise do the entire row - if (columnsToUpdate) { - this.summaryFeature.view.updateColumns(destSummaryRow, sourceSummaryRow, columnsToUpdate); - } else { - Ext.fly(destSummaryRow).syncContent(sourceSummaryRow); - } - } - } - } - ], - init: function(grid) { - var me = this, - view = me.view, - dock = me.dock; - me.callParent(arguments); - if (dock) { - grid.addBodyCls(me.panelBodyCls + dock); - grid.headerCt.on({ - add: me.onStoreUpdate, - afterlayout: me.onStoreUpdate, - scope: me - }); - grid.on({ - beforerender: function() { - var tableCls = [ - me.summaryTableCls - ]; - if (view.columnLines) { - tableCls[tableCls.length] = view.ownerCt.colLinesCls; - } - me.summaryBar = grid.addDocked({ - childEls: [ - 'innerCt', - 'item' - ], - renderTpl: [ - '
    ` HTML tag to its output stream. - * - * The `cellTpl's` data object looks like this: - * - * { - * record: recordToRender - * column: columnToRender; - * recordIndex: indexOfRecordInStore, - * rowIndex: indexOfRowInView, - * columnIndex: columnIndex, - * align: columnAlign, - * tdCls: classForCell - * } - * - * A Feature may inject its own tpl or rowTpl or cellTpl into the {@link Ext.view.Table TableView}'s rendering by - * calling {@link Ext.view.Table#addTpl} or {@link Ext.view.Table#addRowTpl} or {@link Ext.view.Table#addCellTpl}. - * - * The passed XTemplate is added *upstream* of the default template for the table element in a link list of XTemplates which contribute - * to the element's HTML. It may emit appropriate HTML strings into the output stream *around* a call to - * - * this.nextTpl.apply(values, out, parent); - * - * This passes the current value context, output stream and the parent value context to the next XTemplate in the list. - * - * @abstract - */ -Ext.define('Ext.grid.feature.Feature', { - extend: 'Ext.util.Observable', - alias: 'feature.feature', - wrapsItem: false, - /** - * @property {Boolean} isFeature - * `true` in this class to identify an object as an instantiated Feature, or subclass thereof. - */ - isFeature: true, - /** - * True when feature is disabled. - */ - disabled: false, - /** - * @property {Boolean} - * Most features will expose additional events, some may not and will - * need to change this to false. - */ - hasFeatureEvent: true, - /** - * @property {String} - * Prefix to use when firing events on the view. - * For example a prefix of group would expose "groupclick", "groupcontextmenu", "groupdblclick". - */ - eventPrefix: null, - /** - * @property {String} - * Selector used to determine when to fire the event with the eventPrefix. - */ - eventSelector: null, - /** - * @property {Ext.view.Table} - * Reference to the TableView. - */ - view: null, - /** - * @property {Ext.grid.Panel} - * Reference to the grid panel - */ - grid: null, - constructor: function(config) { - this.initialConfig = config; - this.callParent(arguments); - }, - clone: function() { - return new this.self(this.initialConfig); - }, - /** - * Protected method called during {@link Ext.view.Table View} construction. The - * owning {@link Ext.grid.Panel Grid} is passed as a param. - * @param {Ext.grid.Panel} grid The View's owning Grid. **Note** that in a - * {@link Ext.grid.Panel#cfg-enableLocking locking Grid} the passed grid will be - * either the normal grid or the locked grid, which is the view's direct owner. - * @method - * @protected - */ - init: Ext.emptyFn, - /** - * Abstract method to be overriden when a feature should add additional - * arguments to its event signature. By default the event will fire: - * - * - view - The underlying Ext.view.Table - * - featureTarget - The matched element by the defined {@link #eventSelector} - * - * The method must also return the eventName as the first index of the array - * to be passed to fireEvent. - * @template - */ - getFireEventArgs: function(eventName, view, featureTarget, e) { - return [ - eventName, - view, - featureTarget, - e - ]; - }, - vetoEvent: Ext.emptyFn, - /** - * Enables the feature. - */ - enable: function() { - this.disabled = false; - }, - /** - * Disables the feature. - */ - disable: function() { - this.disabled = true; - } -}); - -/** - * A small abstract class that contains the shared behaviour for any summary - * calculations to be used in the grid. - */ -Ext.define('Ext.grid.feature.AbstractSummary', { - extend: 'Ext.grid.feature.Feature', - alias: 'feature.abstractsummary', - summaryRowCls: Ext.baseCSSPrefix + 'grid-row-summary', - readDataOptions: { - recordCreator: Ext.identityFn - }, - // High priority rowTpl interceptor which sees summary rows early, and renders them correctly and then aborts the row rendering chain. - // This will only see action when summary rows are being updated and Table.onUpdate->Table.bufferRender renders the individual updated sumary row. - summaryRowTpl: { - fn: function(out, values, parent) { - // If a summary record comes through the rendering pipeline, render it simply instead of proceeding through the tplchain - if (values.record.isSummary && this.summaryFeature.showSummaryRow) { - this.summaryFeature.outputSummaryRecord(values.record, values, out, parent); - } else { - this.nextTpl.applyOut(values, out, parent); - } - }, - priority: 1000 - }, - /** - * @cfg {Boolean} - * True to show the summary row. - */ - showSummaryRow: true, - // Listen for store updates. Eg, from an Editor. - init: function() { - var me = this; - me.view.summaryFeature = me; - me.rowTpl = me.view.self.prototype.rowTpl; - // Add a high priority interceptor which renders summary records simply - // This will only see action ona bufferedRender situation where summary records are updated. - me.view.addRowTpl(me.summaryRowTpl).summaryFeature = me; - // Define on the instance to store info needed by summary renderers. - me.summaryData = {}; - me.groupInfo = {}; - // Cell widths in the summary table are set directly into the cells. There's no
    ', - '{%', - // Group title is visible if not locking, or we are the locked side, or the locked side has no columns/ - // Use visibility to keep row heights synced without intervention. - 'var groupTitleStyle = (!values.view.lockingPartner || (values.view.ownerCt === values.view.ownerCt.ownerLockable.lockedGrid) || (values.view.lockingPartner.headerCt.getVisibleGridColumns().length === 0)) ? "" : "visibility:hidden";', - '%}', - // TODO. Make the group header tabbable with tabIndex="0" and enable grid navigation "Action Mode" - // to activate it. - '
    ', - '
    ', - '{[values.groupHeaderTpl.apply(values.metaGroupCache, parent) || " "]}', - '
    ', - '
    ', - '
    ', - '
    {rowBody}
    ', - '
    ', - '', - '
    ', - '
    ' - ], - scrollable: { - x: false, - y: false - }, - hidden: !me.showSummaryRow, - itemId: 'summaryBar', - cls: [ - me.dockedSummaryCls, - me.dockedSummaryCls + '-' + dock - ], - xtype: 'component', - dock: dock, - weight: 10000000 - })[0]; - }, - afterrender: function() { - grid.getView().getScrollable().addPartner(me.summaryBar.getScrollable()); - me.onStoreUpdate(); - }, - single: true - }); - // Stretch the innerCt of the summary bar upon headerCt layout - grid.headerCt.afterComponentLayout = Ext.Function.createSequence(grid.headerCt.afterComponentLayout, function() { - var width = this.getTableWidth(), - innerCt = me.summaryBar.innerCt; - me.summaryBar.item.setWidth(width); - // "this" is the HeaderContainer. Its tooNarrow flag is set by its layout if the columns overflow. - // Must not measure+set in after layout phase, this is a write phase. - if (this.tooNarrow) { - width += Ext.getScrollbarSize().width; - } - innerCt.setWidth(width); - }); - } else { - if (grid.bufferedRenderer) { - me.wrapsItem = true; - view.addRowTpl(Ext.XTemplate.getTpl(me, 'fullSummaryTpl')).summaryFeature = me; - view.on('refresh', me.onViewRefresh, me); - } else { - me.wrapsItem = false; - me.view.addFooterFn(me.renderSummaryRow); - } - } - grid.ownerGrid.on({ - beforereconfigure: me.onBeforeReconfigure, - columnmove: me.onStoreUpdate, - scope: me - }); - me.bindStore(grid, grid.getStore()); - }, - onBeforeReconfigure: function(grid, store) { - this.summaryRecord = null; - if (store) { - this.bindStore(grid, store); - } - }, - bindStore: function(grid, store) { - var me = this; - Ext.destroy(me.storeListeners); - me.storeListeners = store.on({ - scope: me, - destroyable: true, - update: me.onStoreUpdate, - datachanged: me.onStoreUpdate - }); - me.callParent([ - grid, - store - ]); - }, - renderSummaryRow: function(values, out, parent) { - var view = values.view, - me = view.findFeature('summary'), - record, rows; - // If we get to here we won't be buffered - if (!me.disabled && me.showSummaryRow) { - record = me.summaryRecord; - out.push(''); - me.outputSummaryRecord((record && record.isModel) ? record : me.createSummaryRecord(view), values, out, parent); - out.push('
    '); - } - }, - toggleSummaryRow: function(visible, /* private */ - fromLockingPartner) { - var me = this, - bar = me.summaryBar; - me.callParent([ - visible, - fromLockingPartner - ]); - if (bar) { - bar.setVisible(me.showSummaryRow); - me.onViewScroll(); - } - }, - getSummaryBar: function() { - return this.summaryBar; - }, - vetoEvent: function(record, row, rowIndex, e) { - return !e.getTarget(this.summaryRowSelector); - }, - onViewScroll: function() { - this.summaryBar.setScrollX(this.view.getScrollX()); - }, - onViewRefresh: function(view) { - var me = this, - record, row; - // Only add this listener if in buffered mode, if there are no rows then - // we won't have anything rendered, so we need to push the row in here - if (!me.disabled && me.showSummaryRow && !view.all.getCount()) { - record = me.createSummaryRecord(view); - row = Ext.fly(view.getNodeContainer()).createChild({ - tag: 'table', - cellpadding: 0, - cellspacing: 0, - cls: me.summaryItemCls, - style: 'table-layout: fixed; width: 100%' - }, false, true); - row.appendChild(Ext.fly(view.createRowElement(record, -1)).down(me.summaryRowSelector, true)); - } - }, - createSummaryRecord: function(view) { - var me = this, - columns = view.headerCt.getGridColumns(), - remoteRoot = me.remoteRoot, - summaryRecord = me.summaryRecord, - colCount = columns.length, - i, column, dataIndex, summaryValue, modelData; - if (!summaryRecord) { - modelData = { - id: view.id + '-summary-record' - }; - summaryRecord = me.summaryRecord = new Ext.data.Model(modelData); - } - // Set the summary field values - summaryRecord.beginEdit(); - if (remoteRoot) { - summaryValue = me.generateSummaryData(); - if (summaryValue) { - summaryRecord.set(summaryValue); - } - } else { - for (i = 0; i < colCount; i++) { - column = columns[i]; - // In summary records, if there's no dataIndex, then the value in regular rows must come from a renderer. - // We set the data value in using the column ID. - dataIndex = column.dataIndex || column.getItemId(); - // We need to capture this value because it could get overwritten when setting on the model if there - // is a convert() method on the model. - summaryValue = me.getSummary(view.store, column.summaryType, dataIndex); - summaryRecord.set(dataIndex, summaryValue); - // Capture the columnId:value for the summaryRenderer in the summaryData object. - me.setSummaryData(summaryRecord, column.getItemId(), summaryValue); - } - } - summaryRecord.endEdit(true); - // It's not dirty - summaryRecord.commit(true); - summaryRecord.isSummary = true; - return summaryRecord; - }, - onStoreUpdate: function() { - var me = this, - view = me.view, - selector = me.summaryRowSelector, - dock = me.dock, - record, newRowDom, oldRowDom, p; - if (!view.rendered) { - return; - } - record = me.createSummaryRecord(view); - newRowDom = Ext.fly(view.createRowElement(record, -1)).down(selector, true); - if (!newRowDom) { - return; - } - // Summary row is inside the docked summaryBar Component - if (dock) { - p = me.summaryBar.item.dom.firstChild; - oldRowDom = p.firstChild; - } else // Summary row is a regular row in a THEAD inside the View. - // Downlinked through the summary record's ID - { - oldRowDom = me.view.el.down(selector, true); - // If the old row doesn't exist, it means that the store update we are - // reacting to is a remove of the last row. So we will be appending - // to the node container. - p = oldRowDom ? oldRowDom.parentNode : view.getNodeContainer(); - } - if (p) { - p.insertBefore(newRowDom, oldRowDom); - if (oldRowDom) { - p.removeChild(oldRowDom); - } - } - // If docked, the updated row will need sizing because it's outside the View - if (dock) { - me.onColumnHeaderLayout(); - } - }, - // Synchronize column widths in the docked summary Component - onColumnHeaderLayout: function() { - var view = this.view, - columns = view.headerCt.getVisibleGridColumns(), - column, - len = columns.length, - i, - summaryEl = this.summaryBar.el, - el; - for (i = 0; i < len; i++) { - column = columns[i]; - el = summaryEl.down(view.getCellSelector(column), true); - if (el) { - Ext.fly(el).setWidth(column.width || (column.lastBox ? column.lastBox.width : 100)); - } - } - }, - destroy: function() { - var me = this; - me.summaryRecord = me.storeListeners = Ext.destroy(me.storeListeners); - me.callParent(); - } -}); - -/** - * A base class for all menu items that require menu-related functionality such as click handling, - * sub-menus, icons, etc. - * - * @example - * Ext.create('Ext.menu.Menu', { - * width: 100, - * height: 100, - * floating: false, // usually you want this set to True (default) - * renderTo: Ext.getBody(), // usually rendered by it's containing component - * items: [{ - * text: 'icon item', - * iconCls: 'add16' - * },{ - * text: 'text item' - * },{ - * text: 'plain item', - * plain: true - * }] - * }); - */ -Ext.define('Ext.menu.Item', { - extend: 'Ext.Component', - alias: 'widget.menuitem', - alternateClassName: 'Ext.menu.TextItem', - /** - * @property {Boolean} isMenuItem - * `true` in this class to identify an object as an instantiated Menu Item, or subclass thereof. - */ - isMenuItem: true, - mixins: [ - 'Ext.mixin.Queryable' - ], - /** - * @property {Boolean} activated - * Whether or not this item is currently activated - */ - activated: false, - /** - * @property {Ext.menu.Menu} parentMenu - * The parent Menu of this item. - */ - /** - * @cfg {String} activeCls - * The CSS class added to the menu item when the item is focused. - */ - activeCls: Ext.baseCSSPrefix + 'menu-item-active', - /** - * @cfg {Boolean} canActivate - * Whether or not this menu item can be focused. - * @deprecated 5.1.0 Use the {@link #focusable} config. - */ - /** - * @cfg {Number} clickHideDelay - * The delay in milliseconds to wait before hiding the menu after clicking the menu item. - * This only has an effect when `hideOnClick: true`. - */ - clickHideDelay: 0, - /** - * @cfg {Boolean} destroyMenu - * Whether or not to destroy any associated sub-menu when this item is destroyed. - */ - destroyMenu: true, - /** - * @cfg {String} disabledCls - * The CSS class added to the menu item when the item is disabled. - */ - disabledCls: Ext.baseCSSPrefix + 'menu-item-disabled', - /** - * @cfg {String} [href='#'] - * The href attribute to use for the underlying anchor link. - */ - /** - * @cfg {String} hrefTarget - * The target attribute to use for the underlying anchor link. - */ - /** - * @cfg {Boolean} hideOnClick - * Whether to not to hide the owning menu when this item is clicked. - */ - hideOnClick: true, - /** - * @cfg {String} [icon=Ext#BLANK_IMAGE_URL] - * @inheritdoc Ext.panel.Header#icon - */ - /** - * @cfg {String} iconCls - * @inheritdoc Ext.panel.Header#cfg-iconCls - */ - /** - * @cfg {Number/String} glyph - * @inheritdoc Ext.panel.Header#glyph - */ - /** - * @cfg {Ext.menu.Menu/Object} menu - * Either an instance of {@link Ext.menu.Menu} or a config object for an {@link Ext.menu.Menu} - * which will act as a sub-menu to this item. - */ - /** - * @property {Ext.menu.Menu} menu The sub-menu associated with this item, if one was configured. - */ - /** - * @cfg {String} menuAlign - * The default {@link Ext.util.Positionable#getAlignToXY Ext.util.Positionable.getAlignToXY} anchor position value for this - * item's sub-menu relative to this item's position. - */ - menuAlign: 'tl-tr?', - /** - * @cfg {Number} menuExpandDelay - * The delay in milliseconds before this item's sub-menu expands after this item is moused over. - */ - menuExpandDelay: 200, - /** - * @cfg {Number} menuHideDelay - * The delay in milliseconds before this item's sub-menu hides after this item is moused out. - */ - menuHideDelay: 200, - /** - * @cfg {Boolean} plain - * Whether or not this item is plain text/html with no icon or visual submenu indication. - */ - /** - * @cfg {String/Object} tooltip - * The tooltip for the button - can be a string to be used as innerHTML (html tags are accepted) or - * QuickTips config object. - */ - /** - * @cfg {String} tooltipType - * The type of tooltip to use. Either 'qtip' for QuickTips or 'title' for title attribute. - */ - tooltipType: 'qtip', - focusable: true, - ariaRole: 'menuitem', - ariaEl: 'itemEl', - baseCls: Ext.baseCSSPrefix + 'menu-item', - arrowCls: Ext.baseCSSPrefix + 'menu-item-arrow', - baseIconCls: Ext.baseCSSPrefix + 'menu-item-icon', - textCls: Ext.baseCSSPrefix + 'menu-item-text', - indentCls: Ext.baseCSSPrefix + 'menu-item-indent', - indentNoSeparatorCls: Ext.baseCSSPrefix + 'menu-item-indent-no-separator', - indentRightIconCls: Ext.baseCSSPrefix + 'menu-item-indent-right-icon', - indentRightArrowCls: Ext.baseCSSPrefix + 'menu-item-indent-right-arrow', - linkCls: Ext.baseCSSPrefix + 'menu-item-link', - linkHrefCls: Ext.baseCSSPrefix + 'menu-item-link-href', - childEls: [ - 'itemEl', - 'iconEl', - 'textEl', - 'arrowEl' - ], - renderTpl: '' + '{text}' + '' + ' {linkHrefCls}{childElCls}"' + ' href="{href}" ' + ' target="{hrefTarget}"' + ' hidefocus="true"' + // For most browsers the text is already unselectable but Opera needs an explicit unselectable="on". - ' unselectable="on"' + '' + ' tabindex="{tabIndex}"' + '' + ' {$}="{.}"' + '>' + '{text}' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '', - maskOnDisable: false, - iconAlign: 'left', - /** - * @cfg {String} text - * The text/html to display in this item. - */ - /** - * @cfg {Function/String} handler - * A function called when the menu item is clicked (can be used instead of {@link #click} event). - * @cfg {Ext.menu.Item} handler.item The item that was clicked - * @cfg {Ext.event.Event} handler.e The underlying {@link Ext.event.Event}. - * @declarativeHandler - */ - /** - * @event activate - * Fires when this item is activated - * @param {Ext.menu.Item} item The activated item - */ - /** - * @event click - * Fires when this item is clicked - * @param {Ext.menu.Item} item The item that was clicked - * @param {Ext.event.Event} e The underlying {@link Ext.event.Event}. - */ - /** - * @event deactivate - * Fires when this item is deactivated - * @param {Ext.menu.Item} item The deactivated item - */ - /** - * @event textchange - * Fired when the item's text is changed by the {@link #setText} method. - * @param {Ext.menu.Item} this - * @param {String} oldText - * @param {String} newText - */ - /** - * @event iconchange - * Fired when the item's icon is changed by the {@link #setIcon} or {@link #setIconCls} methods. - * @param {Ext.menu.Item} this - * @param {String} oldIcon - * @param {String} newIcon - */ - initComponent: function() { - var me = this, - cls = me.cls ? [ - me.cls - ] : [], - menu; - // During deprecation period of canActivate config, copy it into focusable config. - if (me.hasOwnProperty('canActivate')) { - me.focusable = me.canActivate; - } - if (me.plain) { - cls.push(Ext.baseCSSPrefix + 'menu-item-plain'); - } - if (cls.length) { - me.cls = cls.join(' '); - } - if (me.menu) { - menu = me.menu; - me.menu = null; - me.setMenu(menu); - } - me.callParent(arguments); - }, - canFocus: function() { - var me = this; - // This is an override of the implementation in Focusable. - // We do not refuse focus if the Item is disabled. - // http://www.w3.org/TR/2013/WD-wai-aria-practices-20130307/#menu - // "Disabled menu items receive focus but have no action when Enter or Left Arrow/Right Arrow is pressed." - // Test that deprecated canActivate config has not been set to false. - return me.focusable && me.rendered && me.canActivate !== false && !me.destroying && !me.destroyed && me.isVisible(true); - }, - onFocus: function(e) { - var me = this; - me.callParent([ - e - ]); - if (!me.disabled) { - if (!me.plain) { - me.addCls(me.activeCls); - } - me.activated = true; - if (me.hasListeners.activate) { - me.fireEvent('activate', me); - } - } - }, - onFocusLeave: function(e) { - var me = this; - me.callParent([ - e - ]); - if (me.activated) { - if (!me.plain) { - me.removeCls(me.activeCls); - } - me.doHideMenu(); - me.activated = false; - if (me.hasListeners.deactivate) { - me.fireEvent('deactivate', me); - } - } - }, - doHideMenu: function() { - var menu = this.menu; - this.cancelDeferExpand(); - if (menu && menu.isVisible()) { - menu.hide(); - } - }, - /** - * @private - * Hides the entire floating menu tree that we are within. - * Walks up the refOwner axis hiding each Menu instance it find until it hits - * a non-floating ancestor. - */ - deferHideParentMenus: function() { - for (var menu = this.getRefOwner(); menu && ((menu.isMenu && menu.floating) || menu.isMenuItem); menu = menu.getRefOwner()) { - if (menu.isMenu) { - menu.hide(); - } - } - }, - expandMenu: function(event, delay) { - var me = this; - if (me.activated && me.menu) { - // hideOnClick makes no sense when there's a child menu - me.hideOnClick = false; - me.cancelDeferHide(); - // Allow configuration of zero to perform immediate expansion. - delay = delay == null ? me.menuExpandDelay : delay; - if (delay === 0) { - me.doExpandMenu(event); - } else { - me.cancelDeferExpand(); - // Delay can't be 0 by this point - me.expandMenuTimer = Ext.defer(me.doExpandMenu, delay, me, [ - event - ]); - } - } - }, - doExpandMenu: function(clickEvent) { - var me = this, - menu = me.menu; - if (!menu.isVisible()) { - me.parentMenu.activeChild = menu; - menu.ownerCmp = me; - menu.parentMenu = me.parentMenu; - menu.constrainTo = document.body; - // Pointer-invoked menus do not auto focus, key invoked ones do. - menu.autoFocus = !clickEvent || !clickEvent.pointerType; - menu.showBy(me, me.menuAlign); - } - }, - getRefItems: function(deep) { - var menu = this.menu, - items; - if (menu) { - items = menu.getRefItems(deep); - items.unshift(menu); - } - return items || []; - }, - getValue: function() { - return this.value; - }, - hideMenu: function(delay) { - var me = this; - if (me.menu) { - me.cancelDeferExpand(); - me.hideMenuTimer = Ext.defer(me.doHideMenu, Ext.isNumber(delay) ? delay : me.menuHideDelay, me); - } - }, - onClick: function(e) { - var me = this, - clickHideDelay = me.clickHideDelay, - browserEvent = e.browserEvent, - clickResult, preventDefault; - if (!me.href || me.disabled) { - e.stopEvent(); - if (me.disabled) { - return false; - } - } - if (me.disabled || me.handlingClick) { - return; - } - if (me.hideOnClick) { - // on mobile webkit, when the menu item has an href, a longpress will - // trigger the touch call-out menu to show. If this is the case, the tap - // event object's browser event type will be 'touchcancel', and we do not - // want to hide the menu. - // items with submenus are activated by touchstart on mobile browsers, so - // we cannot hide the menu on "tap" - if (!clickHideDelay) { - me.deferHideParentMenus(); - } else { - me.deferHideParentMenusTimer = Ext.defer(me.deferHideParentMenus, clickHideDelay, me); - } - } - // Click event may have destroyed the menu, don't do anything further - clickResult = me.fireEvent('click', me, e); - if (me.destroyed) { - return; - } - if (clickResult !== false && me.handler) { - Ext.callback(me.handler, me.scope, [ - me, - e - ], 0, me); - } - // If there's an href, invoke dom.click() after we've fired the click event in case a click - // listener wants to handle it. - // - // Note that we're having to do this because the key navigation code will blindly call stopEvent() - // on all key events that it handles! - // - // But, we need to check the browser event object that was passed to the listeners to determine if - // the default action has been prevented. If so, we don't want to honor the .href config. - if (Ext.isIE9m) { - // Here we need to invert the value since it's meaning is the opposite of defaultPrevented. - preventDefault = browserEvent.returnValue === false ? true : false; - } else { - preventDefault = !!browserEvent.defaultPrevented; - } - // We only manually need to trigger the click event if it's come from a key event. - if (me.href && e.type !== 'click' && !preventDefault) { - me.handlingClick = true; - me.itemEl.dom.click(); - me.handlingClick = false; - } - if (!me.hideOnClick) { - me.focus(); - } - return clickResult; - }, - onRemoved: function() { - var me = this; - // Removing the active item, must deactivate it. - if (me.activated && me.parentMenu.activeItem === me) { - me.parentMenu.deactivateActiveItem(); - } - me.callParent(arguments); - me.parentMenu = me.ownerCmp = null; - }, - /** - * @private - */ - beforeDestroy: function() { - var me = this; - if (me.rendered) { - me.clearTip(); - } - me.callParent(); - }, - onDestroy: function() { - var me = this; - me.cancelDeferExpand(); - me.cancelDeferHide(); - clearTimeout(me.deferHideParentMenusTimer); - me.setMenu(null); - me.callParent(arguments); - }, - beforeRender: function() { - var me = this, - glyph = me.glyph, - glyphFontFamily = Ext._glyphFontFamily, - hasIcon = !!(me.icon || me.iconCls || glyph), - hasMenu = !!me.menu, - rightIcon = ((me.iconAlign === 'right') && !hasMenu), - isCheckItem = me.isMenuCheckItem, - indentCls = [], - ownerCt = me.ownerCt, - isOwnerPlain = ownerCt.plain, - glyphParts; - if (me.plain) { - me.ariaEl = 'el'; - } - me.callParent(); - if (hasIcon) { - if (hasMenu && me.showCheckbox) { - // nowhere to put the icon, menu arrow on one side, checkbox on the other. - // TODO: maybe put the icon or checkbox next to the arrow? - hasIcon = false; - } - } - if (typeof glyph === 'string') { - glyphParts = glyph.split('@'); - glyph = glyphParts[0]; - glyphFontFamily = glyphParts[1]; - } - if (!isOwnerPlain || (hasIcon && !rightIcon) || isCheckItem) { - if (ownerCt.showSeparator && !isOwnerPlain) { - indentCls.push(me.indentCls); - } else { - indentCls.push(me.indentNoSeparatorCls); - } - } - if (hasMenu) { - indentCls.push(me.indentRightArrowCls); - } else if (hasIcon && (rightIcon || isCheckItem)) { - indentCls.push(me.indentRightIconCls); - } - Ext.applyIf(me.renderData, { - hasHref: !!me.href, - href: me.href || '#', - hrefTarget: me.hrefTarget, - icon: me.icon, - iconCls: me.iconCls, - glyph: glyph, - glyphCls: glyph ? Ext.baseCSSPrefix + 'menu-item-glyph' : undefined, - glyphFontFamily: glyphFontFamily, - hasIcon: hasIcon, - hasMenu: hasMenu, - indent: !isOwnerPlain || hasIcon || isCheckItem, - isCheckItem: isCheckItem, - rightIcon: rightIcon, - plain: me.plain, - text: me.text, - arrowCls: me.arrowCls, - baseIconCls: me.baseIconCls, - textCls: me.textCls, - indentCls: indentCls.join(' '), - linkCls: me.linkCls, - linkHrefCls: me.linkHrefCls, - groupCls: me.group ? me.groupCls : '', - tabIndex: me.tabIndex - }); - }, - onRender: function() { - var me = this; - me.callParent(arguments); - if (me.tooltip) { - me.setTooltip(me.tooltip, true); - } - }, - /** - * Get the attached sub-menu for this item. - * @return {Ext.menu.Menu} The sub-menu. `null` if it doesn't exist. - */ - getMenu: function() { - return this.menu || null; - }, - /** - * Set a child menu for this item. See the {@link #cfg-menu} configuration. - * @param {Ext.menu.Menu/Object} menu A menu, or menu configuration. null may be - * passed to remove the menu. - * @param {Boolean} [destroyMenu] True to destroy any existing menu. False to - * prevent destruction. If not specified, the {@link #destroyMenu} configuration - * will be used. - */ - setMenu: function(menu, destroyMenu) { - var me = this, - oldMenu = me.menu, - arrowEl = me.arrowEl, - ariaDom = me.ariaEl.dom, - ariaAttr, instanced; - if (oldMenu) { - oldMenu.ownerCmp = oldMenu.parentMenu = null; - if (destroyMenu === true || (destroyMenu !== false && me.destroyMenu)) { - Ext.destroy(oldMenu); - } - if (ariaDom) { - ariaDom.removeAttribute('aria-haspopup'); - ariaDom.removeAttribute('aria-owns'); - } else { - ariaAttr = (me.ariaRenderAttributes || (me.ariaRenderAttributes = {})); - delete ariaAttr['aria-haspopup']; - delete ariaAttr['aria-owns']; - } - } - if (menu) { - instanced = menu.isMenu; - menu = me.menu = Ext.menu.Manager.get(menu, { - ownerCmp: me, - focusOnToFront: false - }); - // We need to forcibly set this here because we could be passed - // an existing menu, which means the config above won't get applied - // during creation. - menu.setOwnerCmp(me, instanced); - if (ariaDom) { - ariaDom.setAttribute('aria-haspopup', true); - ariaDom.setAttribute('aria-owns', menu.id); - } else { - ariaAttr = (me.ariaRenderAttributes || (me.ariaRenderAttributes = {})); - ariaAttr['aria-haspopup'] = true; - ariaAttr['aria-owns'] = menu.id; - } - } else { - menu = me.menu = null; - } - if (menu && me.rendered && !me.destroying && arrowEl) { - arrowEl[menu ? 'addCls' : 'removeCls'](me.arrowCls); - } - }, - /** - * Sets the {@link #click} handler of this item - * @param {Function} fn The handler function - * @param {Object} [scope] The scope of the handler function - */ - setHandler: function(fn, scope) { - this.handler = fn || null; - this.scope = scope; - }, - /** - * Sets the {@link #icon} on this item. - * @param {String} icon The new icon - */ - setIcon: function(icon) { - var iconEl = this.iconEl, - oldIcon = this.icon; - if (iconEl) { - iconEl.src = icon || Ext.BLANK_IMAGE_URL; - } - this.icon = icon; - this.fireEvent('iconchange', this, oldIcon, icon); - }, - /** - * Sets the {@link #iconCls} of this item - * @param {String} iconCls The CSS class to set to {@link #iconCls} - */ - setIconCls: function(iconCls) { - var me = this, - iconEl = me.iconEl, - oldCls = me.iconCls; - if (iconEl) { - if (me.iconCls) { - iconEl.removeCls(me.iconCls); - } - if (iconCls) { - iconEl.addCls(iconCls); - } - } - me.iconCls = iconCls; - me.fireEvent('iconchange', me, oldCls, iconCls); - }, - /** - * Sets the {@link #text} of this item - * @param {String} text The {@link #text} - */ - setText: function(text) { - var me = this, - el = me.textEl || me.el, - oldText = me.text; - me.text = text; - if (me.rendered) { - el.setHtml(text || ''); - me.updateLayout(); - } - me.fireEvent('textchange', me, oldText, text); - }, - getTipAttr: function() { - return this.tooltipType === 'qtip' ? 'data-qtip' : 'title'; - }, - /** - * @private - */ - clearTip: function() { - if (Ext.quickTipsActive && Ext.isObject(this.tooltip)) { - Ext.tip.QuickTipManager.unregister(this.itemEl); - } - }, - /** - * Sets the tooltip for this menu item. - * - * @param {String/Object} tooltip This may be: - * - * - **String** : A string to be used as innerHTML (html tags are accepted) to show in a tooltip - * - **Object** : A configuration object for {@link Ext.tip.QuickTipManager#register}. - * - * @return {Ext.menu.Item} this - */ - setTooltip: function(tooltip, initial) { - var me = this; - if (me.rendered) { - if (!initial) { - me.clearTip(); - } - if (Ext.quickTipsActive && Ext.isObject(tooltip)) { - Ext.tip.QuickTipManager.register(Ext.apply({ - target: me.itemEl.id - }, tooltip)); - me.tooltip = tooltip; - } else { - me.itemEl.dom.setAttribute(me.getTipAttr(), tooltip); - } - } else { - me.tooltip = tooltip; - } - return me; - }, - privates: { - cancelDeferExpand: function() { - window.clearTimeout(this.expandMenuTimer); - }, - cancelDeferHide: function() { - window.clearTimeout(this.hideMenuTimer); - }, - getFocusEl: function() { - return this.plain ? this.el : this.itemEl; - } - } -}); - -/** - * A menu item that contains a togglable checkbox by default, but that can also be a part of a radio group. - * - * @example - * Ext.create('Ext.menu.Menu', { - * width: 100, - * height: 110, - * floating: false, // usually you want this set to True (default) - * renderTo: Ext.getBody(), // usually rendered by it's containing component - * items: [{ - * xtype: 'menucheckitem', - * text: 'select all' - * },{ - * xtype: 'menucheckitem', - * text: 'select specific' - * },{ - * iconCls: 'add16', - * text: 'icon item' - * },{ - * text: 'regular item' - * }] - * }); - */ -Ext.define('Ext.menu.CheckItem', { - extend: 'Ext.menu.Item', - alias: 'widget.menucheckitem', - /** - * @cfg {Boolean} [checked=false] - * True to render the menuitem initially checked. - */ - /** - * @cfg {Function/String} checkHandler - * Alternative for the {@link #checkchange} event. Gets called with the same parameters. - * @declarativeHandler - */ - /** - * @cfg {Object} scope - * Scope for the {@link #checkHandler} callback. - */ - /** - * @cfg {String} group - * Name of a radio group that the item belongs. - * - * Specifying this option will turn check item into a radio item. - * - * Note that the group name must be globally unique. - */ - /** - * @cfg {String} checkedCls - * The CSS class used by {@link #cls} to show the checked state. - * Defaults to `Ext.baseCSSPrefix + 'menu-item-checked'`. - */ - checkedCls: Ext.baseCSSPrefix + 'menu-item-checked', - /** - * @cfg {String} uncheckedCls - * The CSS class used by {@link #cls} to show the unchecked state. - * Defaults to `Ext.baseCSSPrefix + 'menu-item-unchecked'`. - */ - uncheckedCls: Ext.baseCSSPrefix + 'menu-item-unchecked', - /** - * @cfg {String} groupCls - * The CSS class applied to this item's icon image to denote being a part of a radio group. - * Defaults to `Ext.baseCSSClass + 'menu-group-icon'`. - * Any specified {@link #iconCls} overrides this. - */ - groupCls: Ext.baseCSSPrefix + 'menu-group-icon', - /** - * @cfg {Boolean} [hideOnClick=false] - * Whether to not to hide the owning menu when this item is clicked. - * Defaults to `false` for checkbox items, and to `true` for radio group items. - */ - hideOnClick: false, - /** - * @cfg {Boolean} [checkChangeDisabled=false] - * True to prevent the checked item from being toggled. Any submenu will still be accessible. - */ - checkChangeDisabled: false, - // - /** - * @cfg {String} submenuText Text to be announced by screen readers when a check item - * submenu is focused. - */ - submenuText: '{0} submenu', - // - ariaRole: 'menuitemcheckbox', - childEls: [ - 'checkEl' - ], - showCheckbox: true, - isMenuCheckItem: true, - checkboxCls: Ext.baseCSSPrefix + 'menu-item-checkbox', - /** - * @event beforecheckchange - * Fires before a change event. Return false to cancel. - * @param {Ext.menu.CheckItem} this - * @param {Boolean} checked - */ - /** - * @event checkchange - * Fires after a change event. - * @param {Ext.menu.CheckItem} this - * @param {Boolean} checked - */ - initComponent: function() { - var me = this; - // coerce to bool straight away - me.checked = !!me.checked; - me.callParent(arguments); - if (me.group) { - Ext.menu.Manager.registerCheckable(me); - if (me.initialConfig.hideOnClick !== false) { - me.hideOnClick = true; - } - } - }, - beforeRender: function() { - var me = this, - ariaAttr; - me.callParent(); - Ext.apply(me.renderData, { - checkboxCls: me.checkboxCls, - showCheckbox: me.showCheckbox - }); - ariaAttr = (me.ariaRenderAttributes || (me.ariaRenderAttributes = {})); - ariaAttr['aria-checked'] = me.menu ? 'mixed' : me.checked; - // For some reason JAWS will not announce that a check item has a submenu - // so users will get no indication whatsoever, unless we set the label. - if (me.menu) { - ariaAttr['aria-label'] = Ext.String.formatEncode(me.submenuText, me.text); - } - }, - afterRender: function() { - var me = this; - me.callParent(); - me.checked = !me.checked; - me.setChecked(!me.checked, true); - if (me.checkChangeDisabled) { - me.disableCheckChange(); - } - // For reasons unknown, clicking a div inside anchor element might cause - // the anchor to be blurred in Firefox. We can't allow this to happen - // because blurring will cause focusleave which will hide the menu - // before click event fires. See https://sencha.jira.com/browse/EXTJS-18882 - if (Ext.isGecko && me.checkEl) { - me.checkEl.on('mousedown', me.onMouseDownCheck); - } - }, - /** - * Disables just the checkbox functionality of this menu Item. If this menu item has a submenu, that submenu - * will still be accessible - */ - disableCheckChange: function() { - var me = this, - checkEl = me.checkEl; - if (checkEl) { - checkEl.addCls(me.disabledCls); - } - // In some cases the checkbox will disappear until repainted, see: EXTJSIV-6412 - if (Ext.isIE8 && me.rendered) { - me.el.repaint(); - } - me.checkChangeDisabled = true; - }, - /** - * Re-enables the checkbox functionality of this menu item after having been - * disabled by {@link #disableCheckChange} - */ - enableCheckChange: function() { - var me = this, - checkEl = me.checkEl; - if (checkEl) { - checkEl.removeCls(me.disabledCls); - } - me.checkChangeDisabled = false; - }, - onMouseDownCheck: function(e) { - e.preventDefault(); - }, - onClick: function(e) { - var me = this; - // If pointer type is touch, we should only toggle check status if there's no submenu or they tapped in the checkEl - // This is because there's no hover to invoke the submenu on touch devices, so a tap is needed to show it. That tap - // should not toggle unless it's on the checkbox. - if (!(me.disabled || me.checkChangeDisabled || me.checked && me.group || me.menu && "touch" === e.pointerType && !me.checkEl.contains(e.target))) { - me.setChecked(!me.checked); - // Clicked using SPACE or ENTER just un-checks. - // RightArrow to invoke any submenu - if (e.type === 'keydown' && me.menu) { - return false; - } - } - this.callParent([ - e - ]); - }, - onDestroy: function() { - Ext.menu.Manager.unregisterCheckable(this); - this.callParent(arguments); - }, - setText: function(text) { - var me = this, - ariaDom = me.ariaEl.dom; - me.callParent([ - text - ]); - if (ariaDom && me.menu) { - ariaDom.setAttribute('aria-label', Ext.String.formatEncode(me.submenuText, text)); - } - }, - /** - * Sets the checked state of the item - * @param {Boolean} checked True to check, false to un-check - * @param {Boolean} [suppressEvents=false] True to prevent firing the checkchange events. - */ - setChecked: function(checked, suppressEvents) { - var me = this, - checkedCls = me.checkedCls, - uncheckedCls = me.uncheckedCls, - el = me.el, - ariaDom = me.ariaEl.dom; - if (me.checked !== checked && (suppressEvents || me.fireEvent('beforecheckchange', me, checked) !== false)) { - if (el) { - if (checked) { - el.addCls(checkedCls); - el.removeCls(uncheckedCls); - } else { - el.addCls(uncheckedCls); - el.removeCls(checkedCls); - } - } - if (ariaDom) { - ariaDom.setAttribute('aria-checked', me.menu ? 'mixed' : !!checked); - } - me.checked = checked; - Ext.menu.Manager.onCheckChange(me, checked); - if (!suppressEvents) { - Ext.callback(me.checkHandler, me.scope, [ - me, - checked - ], 0, me); - me.fireEvent('checkchange', me, checked); - } - } - } -}); - -/** - * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will - * add one of these by using "-" in your call to add() or in your items config rather than creating one directly. - * - * @example - * Ext.create('Ext.menu.Menu', { - * width: 100, - * height: 100, - * floating: false, // usually you want this set to True (default) - * renderTo: Ext.getBody(), // usually rendered by it's containing component - * items: [{ - * text: 'icon item', - * iconCls: 'add16' - * },{ - * xtype: 'menuseparator' - * },{ - * text: 'separator above' - * },{ - * text: 'regular item' - * }] - * }); - */ -Ext.define('Ext.menu.Separator', { - extend: 'Ext.menu.Item', - alias: 'widget.menuseparator', - focusable: false, - /** - * @cfg {String} activeCls - * @private - */ - /** - * @cfg {Boolean} canActivate - * @private - */ - canActivate: false, - /** - * @cfg {Boolean} clickHideDelay - * @private - */ - /** - * @cfg {Boolean} destroyMenu - * @private - */ - /** - * @cfg {Boolean} disabledCls - * @private - */ - /** - * @cfg {String} href - * @private - */ - /** - * @cfg {String} hrefTarget - * @private - */ - /** - * @cfg {Boolean} hideOnClick - * @private - */ - hideOnClick: false, - /** - * @cfg {String} icon - * @private - */ - /** - * @cfg {String} iconCls - * @private - */ - /** - * @cfg {Object} menu - * @private - */ - /** - * @cfg {String} menuAlign - * @private - */ - /** - * @cfg {Number} menuExpandDelay - * @private - */ - /** - * @cfg {Number} menuHideDelay - * @private - */ - /** - * @cfg {Boolean} plain - * @private - */ - plain: true, - /** - * @cfg {String} separatorCls - * The CSS class used by the separator item to show the incised line. - */ - separatorCls: Ext.baseCSSPrefix + 'menu-item-separator', - /** - * @cfg {String} text - * @private - */ - text: ' ', - ariaRole: 'separator', - beforeRender: function() { - this.addCls(this.separatorCls); - this.callParent(); - } -}); - -/** - * A menu object. This is the container to which you may add {@link Ext.menu.Item menu items}. - * - * Menus may contain either {@link Ext.menu.Item menu items}, or general {@link Ext.Component Components}. - * Menus may also contain {@link Ext.panel.Panel#dockedItems docked items} because it extends {@link Ext.panel.Panel}. - * - * By default, non {@link Ext.menu.Item menu items} are indented so that they line up with the text of menu items. clearing - * the icon column. To make a contained general {@link Ext.Component Component} left aligned configure the child - * Component with `indent: false. - * - * By default, Menus are absolutely positioned, floating Components. By configuring a - * Menu with `{@link #cfg-floating}: false`, a Menu may be used as a child of a - * {@link Ext.container.Container Container}. - * - * @example - * Ext.create('Ext.menu.Menu', { - * width: 100, - * margin: '0 0 10 0', - * floating: false, // usually you want this set to True (default) - * renderTo: Ext.getBody(), // usually rendered by it's containing component - * items: [{ - * text: 'regular item 1' - * },{ - * text: 'regular item 2' - * },{ - * text: 'regular item 3' - * }] - * }); - * - * Ext.create('Ext.menu.Menu', { - * width: 100, - * plain: true, - * floating: false, // usually you want this set to True (default) - * renderTo: Ext.getBody(), // usually rendered by it's containing component - * items: [{ - * text: 'plain item 1' - * },{ - * text: 'plain item 2' - * },{ - * text: 'plain item 3' - * }] - * }); - */ -Ext.define('Ext.menu.Menu', { - extend: 'Ext.panel.Panel', - alias: 'widget.menu', - requires: [ - 'Ext.layout.container.VBox', - 'Ext.menu.CheckItem', - 'Ext.menu.Item', - 'Ext.menu.Manager', - 'Ext.menu.Separator' - ], - mixins: [ - 'Ext.util.FocusableContainer' - ], - /** - * @property {Ext.menu.Menu} parentMenu - * The parent Menu of this Menu. - */ - /** - * @cfg {Boolean} [enableKeyNav=true] - * @deprecated 5.1.0 Intra-menu key navigation is always enabled. - */ - enableKeyNav: true, - /** - * @cfg {Boolean} [allowOtherMenus=false] - * True to allow multiple menus to be displayed at the same time. - */ - allowOtherMenus: false, - /** - * @cfg {String} ariaRole - * @private - */ - ariaRole: 'menu', - /** - * @cfg {Boolean} autoRender - * Floating is true, so autoRender always happens. - * @private - */ - /** - * @cfg {Boolean} [floating=true] - * A Menu configured as `floating: true` (the default) will be rendered as an - * absolutely positioned, - * {@link Ext.Component#cfg-floating floating} {@link Ext.Component Component}. If - * configured as `floating: false`, the Menu may be used as a child item of another - * {@link Ext.container.Container Container}. - */ - floating: true, - /** - * @cfg {Boolean} constrain - * Menus are constrained to the document body by default. - * @private - */ - constrain: true, - /** - * @cfg {Boolean} [hidden] - * True to initially render the Menu as hidden, requiring to be shown manually. - * - * Defaults to `true` when `floating: true`, and defaults to `false` when `floating: false`. - */ - hidden: true, - hideMode: 'visibility', - /** - * @cfg {Boolean} [ignoreParentClicks=false] - * True to ignore clicks on any item in this menu that is a parent item (displays a submenu) - * so that the submenu is not dismissed when clicking the parent item. - */ - ignoreParentClicks: false, - /** - * @property {Boolean} isMenu - * `true` in this class to identify an object as an instantiated Menu, or subclass thereof. - */ - isMenu: true, - /** - * @cfg {Ext.enums.Layout/Object} layout - * @private - */ - /** - * @cfg {Boolean} [showSeparator=true] - * True to show the icon separator. - */ - showSeparator: true, - /** - * @cfg {Number} [minWidth=120] - * The minimum width of the Menu. The default minWidth only applies when the - * {@link #cfg-floating} config is true. - */ - minWidth: undefined, - defaultMinWidth: 120, - /** - * @cfg {String} [defaultAlign="tl-bl?"] - * The default {@link Ext.util.Positionable#getAlignToXY Ext.dom.Element#getAlignToXY} anchor position value for this menu - * relative to its owner. Used in conjunction with {@link #showBy}. - */ - defaultAlign: 'tl-bl?', - /** - * @cfg {Boolean} [plain=false] - * True to remove the incised line down the left side of the menu and to not indent general Component items. - * - * {@link Ext.menu.Item MenuItem}s will *always* have space at their start for an icon. With the `plain` setting, - * non {@link Ext.menu.Item MenuItem} child components will not be indented to line up. - * - * Basically, `plain:true` makes a Menu behave more like a regular {@link Ext.layout.container.HBox HBox layout} - * {@link Ext.panel.Panel Panel} which just has the same background as a Menu. - * - * See also the {@link #showSeparator} config. - */ - focusOnToFront: false, - bringParentToFront: false, - defaultFocus: ':focusable', - /** - * @private - */ - menuClickBuffer: 0, - baseCls: Ext.baseCSSPrefix + 'menu', - _iconSeparatorCls: Ext.baseCSSPrefix + 'menu-icon-separator', - _itemCmpCls: Ext.baseCSSPrefix + 'menu-item-cmp', - /** - * @event click - * Fires when this menu is clicked - * @param {Ext.menu.Menu} menu The menu which has been clicked - * @param {Ext.Component} item The menu item that was clicked. `undefined` if not applicable. - * @param {Ext.event.Event} e The underlying {@link Ext.event.Event}. - */ - /** - * @event mouseenter - * Fires when the mouse enters this menu - * @param {Ext.menu.Menu} menu The menu - * @param {Ext.event.Event} e The underlying {@link Ext.event.Event} - */ - /** - * @event mouseleave - * Fires when the mouse leaves this menu - * @param {Ext.menu.Menu} menu The menu - * @param {Ext.event.Event} e The underlying {@link Ext.event.Event} - */ - /** - * @event mouseover - * Fires when the mouse is hovering over this menu - * @param {Ext.menu.Menu} menu The menu - * @param {Ext.Component} item The menu item that the mouse is over. `undefined` if not applicable. - * @param {Ext.event.Event} e The underlying {@link Ext.event.Event} - */ - layout: { - type: 'vbox', - align: 'stretchmax', - overflowHandler: 'Scroller' - }, - initComponent: function() { - var me = this, - cls = [ - Ext.baseCSSPrefix + 'menu' - ], - bodyCls = me.bodyCls ? [ - me.bodyCls - ] : [], - isFloating = me.floating !== false, - listeners = { - element: 'el', - click: me.onClick, - mouseover: me.onMouseOver, - scope: me - }; - if (Ext.supports.Touch) { - listeners.pointerdown = me.onMouseOver; - } - me.on(listeners); - me.on({ - beforeshow: me.onBeforeShow, - scope: me - }); - // Menu classes - if (me.plain) { - cls.push(Ext.baseCSSPrefix + 'menu-plain'); - } - me.cls = cls.join(' '); - // Menu body classes - bodyCls.push(Ext.baseCSSPrefix + 'menu-body', Ext.dom.Element.unselectableCls); - me.bodyCls = bodyCls.join(' '); - if (isFloating) { - // only apply the minWidth when we're floating & one hasn't already been set - if (me.minWidth === undefined) { - me.minWidth = me.defaultMinWidth; - } - } else { - // hidden defaults to false if floating is configured as false - me.hidden = !!me.initialConfig.hidden; - me.constrain = false; - } - me.callParent(arguments); - // Configure items prior to render with special classes to align - // non MenuItem child components with their MenuItem siblings. - Ext.override(me.getLayout(), { - configureItem: me.configureItem - }); - }, - // Private implementation for Menus. They are a special case, in that in the vast majority - // (nearly all?) of use cases they shouldn't be constrained to anything other than the viewport. - // See EXTJS-13596. - /** - * @private - */ - initFloatConstrain: Ext.emptyFn, - getInherited: function() { - // As menus are never contained, a Menu's visibility only ever depends upon its own hidden state. - // Ignore hiddenness from the ancestor hierarchy, override it with local hidden state. - var result = this.callParent(); - result.hidden = this.hidden; - return result; - }, - beforeRender: function() { - var me = this; - me.callParent(arguments); - // Menus are usually floating: true, which means they shrink wrap their items. - // However, when they are contained, and not auto sized, we must stretch the items. - if (!me.getSizeModel().width.shrinkWrap) { - me.layout.align = 'stretch'; - } - if (me.floating) { - me.ariaRenderAttributes = me.ariaRenderAttributes || {}; - me.ariaRenderAttributes['aria-expanded'] = !!me.autoShow; - } - }, - onBoxReady: function() { - var me = this, - iconSeparatorCls = me._iconSeparatorCls, - keyNav = me.focusableKeyNav; - // Keyboard handling can be disabled, e.g. by the DatePicker menu - // or the Date filter menu constructed by the Grid - if (keyNav) { - keyNav.map.processEventScope = me; - keyNav.map.processEvent = function(e) { - // ESC may be from input fields, and FocusableContainers ignore keys from - // input fields. We do not want to ignore ESC. ESC hide menus. - if (e.keyCode === e.ESC) { - e.target = this.el.dom; - } - return e; - }; - // Handle ESC key - keyNav.map.addBinding([ - { - key: Ext.event.Event.ESC, - handler: me.onEscapeKey, - scope: me - }, - // Handle character shortcuts - { - key: /[\w]/, - handler: me.onShortcutKey, - scope: me, - shift: false, - ctrl: false, - alt: false - } - ]); - } else { - // Even when FocusableContainer key event processing is disabled, - // we still need to handle the Escape key! - me.escapeKeyNav = new Ext.util.KeyNav(me.el, { - eventName: 'keydown', - scope: me, - esc: me.onEscapeKey - }); - } - me.callParent(arguments); - // TODO: Move this to a subTemplate When we support them in the future - if (me.showSeparator) { - me.iconSepEl = me.body.insertFirst({ - role: 'presentation', - cls: iconSeparatorCls + ' ' + iconSeparatorCls + '-' + me.ui, - html: ' ' - }); - } - // Modern IE browsers have click events translated to PointerEvents, and b/c of this the - // event isn't being canceled like it needs to be. So, we need to add an extra listener. - if (Ext.supports.MSPointerEvents || Ext.supports.PointerEvents) { - me.el.on({ - scope: me, - click: me.preventClick, - translate: false - }); - } - me.mouseMonitor = me.el.monitorMouseLeave(100, me.onMouseLeave, me); - }, - onFocusLeave: function(e) { - var me = this; - me.callParent([ - e - ]); - me.mixins.focusablecontainer.onFocusLeave.call(me, e); - if (me.floating) { - me.hide(); - } - }, - /** - * @param {Ext.Component} item The child item to test for focusability. - * Returns whether a menu item can be activated or not. - * @return {Boolean} `true` if the passed item is focusable. - */ - canActivateItem: function(item) { - return item && item.isFocusable(); - }, - /** - * Deactivates the current active item on the menu, if one exists. - */ - deactivateActiveItem: function() { - var me = this, - activeItem = me.lastFocusedChild; - if (activeItem) { - activeItem.blur(); - } - }, - /** - * @private - */ - getItemFromEvent: function(e) { - var me = this, - renderTarget = me.layout.getRenderTarget().dom, - toEl = e.getTarget(); - // See which top level element the event is in and find its owning Component. - while (toEl.parentNode !== renderTarget) { - toEl = toEl.parentNode; - if (!toEl) { - return; - } - } - return Ext.getCmp(toEl.id); - }, - lookupComponent: function(cmp) { - var me = this; - if (typeof cmp === 'string') { - cmp = me.lookupItemFromString(cmp); - } else if (Ext.isObject(cmp)) { - cmp = me.lookupItemFromObject(cmp); - } - // Apply our minWidth to all of our non-docked child components (Menu extends Panel) - // so it's accounted for in our VBox layout - if (!cmp.dock) { - cmp.minWidth = cmp.minWidth || me.minWidth; - } - return cmp; - }, - /** - * @private - */ - lookupItemFromObject: function(cmp) { - var me = this; - if (!cmp.isComponent) { - if (!cmp.xtype) { - cmp = Ext.create('Ext.menu.' + (Ext.isBoolean(cmp.checked) ? 'Check' : '') + 'Item', cmp); - } else { - cmp = Ext.ComponentManager.create(cmp, cmp.xtype); - } - } - if (cmp.isMenuItem) { - cmp.parentMenu = me; - } - return cmp; - }, - /** - * @private - */ - lookupItemFromString: function(cmp) { - return (cmp === 'separator' || cmp === '-') ? new Ext.menu.Separator() : new Ext.menu.Item({ - canActivate: false, - hideOnClick: false, - plain: true, - text: cmp - }); - }, - // Override applied to the Menu's layout. Runs in the context of the layout. - // Add special classes to allow non MenuItem components to coexist with MenuItems. - // If there is only *one* child, then this Menu is just a vehicle for floating - // and aligning the component, so do not do this. - configureItem: function(cmp) { - var me = this.owner, - prefix = Ext.baseCSSPrefix, - ui = me.ui, - cls, cmpCls; - if (cmp.isMenuItem) { - cmp.setUI(ui); - } else if (me.items.getCount() > 1 && !cmp.rendered && !cmp.dock) { - cmpCls = me._itemCmpCls; - cls = [ - cmpCls + ' ' + cmpCls + '-' + ui - ]; - // The "plain" setting means that the menu does not look so much like a menu. It's more like a grey Panel. - // So it has no vertical separator. - // Plain menus also will not indent non MenuItem components; there is nothing to indent them to the right of. - if (!me.plain && (cmp.indent !== false || cmp.iconCls === 'no-icon')) { - cls.push(prefix + 'menu-item-indent-' + ui); - } - if (cmp.rendered) { - cmp.el.addCls(cls); - } else { - cmp.cls = (cmp.cls || '') + ' ' + cls.join(' '); - } - // So we can clean the item if it gets removed. - cmp.$extraMenuCls = cls; - } - // @noOptimize.callParent - this.callParent(arguments); - }, - onRemove: function(cmp) { - this.callParent([ - cmp - ]); - // Remove any extra classes we added to non-MenuItem child items - if (!cmp.destroyed && cmp.$extraMenuCls) { - cmp.el.removeCls(cmp.$extraMenuCls); - } - }, - onClick: function(e) { - var me = this, - type = e.type, - item, clickResult, - iskeyEvent = type === 'keydown'; - if (me.disabled) { - e.stopEvent(); - return; - } - item = me.getItemFromEvent(e); - if (item && item.isMenuItem) { - if (!item.menu || !me.ignoreParentClicks) { - clickResult = item.onClick(e); - } else { - e.stopEvent(); - } - // SPACE and ENTER invokes the menu - if (item.menu && clickResult !== false && iskeyEvent) { - item.expandMenu(e, 0); - } - } - // Click event may be fired without an item, so we need a second check - if (!item || item.disabled) { - item = undefined; - } - me.fireEvent('click', me, item, e); - }, - onDestroy: function() { - var me = this; - if (me.escapeKeyNav) { - me.escapeKeyNav.destroy(); - } - me.parentMenu = me.ownerCmp = me.escapeKeyNav = null; - if (me.rendered) { - me.el.un(me.mouseMonitor); - Ext.destroy(me.iconSepEl); - } - // Menu can be destroyed while shown; - // we should notify the Manager - Ext.menu.Manager.onHide(me); - me.callParent(arguments); - }, - onMouseLeave: function(e) { - if (this.disabled) { - return; - } - this.fireEvent('mouseleave', this, e); - }, - onMouseOver: function(e) { - var me = this, - fromEl = e.getRelatedTarget(), - mouseEnter = !me.el.contains(fromEl), - item = me.getItemFromEvent(e), - parentMenu = me.parentMenu, - ownerCmp = me.ownerCmp; - if (mouseEnter && parentMenu) { - parentMenu.setActiveItem(ownerCmp); - ownerCmp.cancelDeferHide(); - parentMenu.mouseMonitor.mouseenter(); - } - if (me.disabled) { - return; - } - // Do not activate the item if the mouseover was within the item, and it's already active - if (item) { - if (!item.containsFocus) { - item.focus(); - } - if (item.expandMenu) { - item.expandMenu(e); - } - } - if (mouseEnter) { - me.fireEvent('mouseenter', me, e); - } - me.fireEvent('mouseover', me, item, e); - }, - setActiveItem: function(item) { - var me = this; - if (item && (item !== me.lastFocusedChild)) { - me.focusChild(item, 1); - } - }, - // Focusing will scroll the item into view. - onEscapeKey: function() { - if (this.floating) { - this.hide(); - } - }, - onShortcutKey: function(keyCode, e) { - var shortcutChar = String.fromCharCode(e.getCharCode()), - items = this.query('>[text]'), - len = items.length, - item = this.lastFocusedChild, - focusIndex = Ext.Array.indexOf(items, item), - i = focusIndex; - // Loop through all items which have a text property starting at the one after the current focus. - for (; ; ) { - if (++i === len) { - i = 0; - } - item = items[i]; - // Looped back to start - no matches - if (i === focusIndex) { - return; - } - // Found a text match - if (item.text && item.text[0].toUpperCase() === shortcutChar) { - item.focus(); - return; - } - } - }, - // Tabbing in a floating menu must hide, but not move focus. - // onHide takes care of moving focus back to an owner Component. - onFocusableContainerTabKey: function(e) { - if (this.floating) { - this.hide(); - } - }, - onFocusableContainerEnterKey: function(e) { - this.onClick(e); - }, - onFocusableContainerSpaceKey: function(e) { - this.onClick(e); - }, - onFocusableContainerLeftKey: function(e) { - // The default action is to scroll the nearest horizontally scrollable container - e.preventDefault(); - // If we are a submenu, then left arrow focuses the owning MenuItem - if (this.parentMenu) { - this.ownerCmp.focus(); - this.hide(); - } - }, - onFocusableContainerRightKey: function(e) { - var me = this, - focusItem = me.lastFocusedChild; - // See above - e.preventDefault(); - if (focusItem && focusItem.expandMenu) { - focusItem.expandMenu(e, 0); - } - }, - onBeforeShow: function() { - // Do not allow show immediately after a hide - if (Ext.Date.getElapsed(this.lastHide) < this.menuClickBuffer) { - return false; - } - }, - beforeShow: function() { - var me = this, - parent, activeEl, viewHeight; - // Constrain the height to the containing element's viewable area - if (me.floating) { - parent = me.hasFloatMenuParent(); - if (!parent && !me.allowOtherMenus) { - Ext.menu.Manager.hideAll(); - } - // Only register a focusAnchor to return to on hide if the active element - // is not the document; if we have a floating parent menu, use its focusAnchor. - // If there's no focusAnchor, we return to the ownerCmp, or first focusable ancestor. - if (parent) { - me.focusAnchor = parent.focusAnchor; - } else { - activeEl = Ext.Element.getActiveElement(); - // IE8 sometimes allow node to focus - if (activeEl === document.body || activeEl === document.documentElement) { - me.focusAnchor = null; - } else { - me.focusAnchor = activeEl; - } - } - me.savedMaxHeight = me.maxHeight; - viewHeight = me.container.getViewSize().height; - me.maxHeight = Math.min(me.maxHeight || viewHeight, viewHeight); - } - me.callParent(arguments); - }, - afterShow: function() { - var me = this, - ariaDom = me.ariaEl.dom; - me.callParent(arguments); - Ext.menu.Manager.onShow(me); - if (me.floating && ariaDom) { - ariaDom.setAttribute('aria-expanded', true); - } - // Restore configured maxHeight - if (me.floating && me.autoFocus) { - me.maxHeight = me.savedMaxHeight; - me.focus(); - } - }, - onHide: function(animateTarget, cb, scope) { - var me = this, - ariaDom = me.ariaEl.dom, - focusTarget; - // If we contain focus just before element hide, move it elsewhere before hiding - if (me.el.contains(Ext.Element.getActiveElement())) { - // focusAnchor was the active element before this menu was shown. - focusTarget = me.focusAnchor || me.ownerCmp || me.up(':focusable'); - // Component hide processing will focus the "previousFocus" element. - if (focusTarget) { - me.previousFocus = focusTarget; - } - } - me.callParent([ - animateTarget, - cb, - scope - ]); - me.lastHide = Ext.Date.now(); - Ext.menu.Manager.onHide(me); - if (me.floating && ariaDom) { - ariaDom.setAttribute('aria-expanded', false); - } - }, - preventClick: function(e) { - var item = this.getItemFromEvent(e); - if (item && item.isMenuItem && !item.href) { - e.preventDefault(); - } - }, - privates: { - hasFloatMenuParent: function() { - return this.parentMenu || this.up('menu[floating=true]'); - }, - setOwnerCmp: function(comp, instanced) { - var me = this; - me.parentMenu = comp.isMenuItem ? comp : null; - me.ownerCmp = comp; - me.registerWithOwnerCt(); - delete me.hierarchicallyHidden; - me.onInheritedAdd(comp, instanced); - me.containerOnAdded(comp, instanced); - } - } -}); - -/** - * Abstract base class for filter implementations. - */ -Ext.define('Ext.grid.filters.filter.Base', { - mixins: [ - 'Ext.mixin.Factoryable' - ], - factoryConfig: { - type: 'grid.filter' - }, - $configPrefixed: false, - $configStrict: false, - config: { - /** - * @cfg {Object} [itemDefaults] - * The default configuration options for any menu items created by this filter. - * - * Example usage: - * - * itemDefaults: { - * width: 150 - * }, - */ - itemDefaults: null, - menuDefaults: { - xtype: 'menu' - }, - /** - * @cfg {Number} updateBuffer - * Number of milliseconds to wait after user interaction to fire an update. Only supported - * by filters: 'list', 'numeric', and 'string'. - */ - updateBuffer: 500 - }, - /** - * @property {Boolean} active - * True if this filter is active. Use setActive() to alter after configuration. If - * you set a value, the filter will be actived automatically. - */ - /** - * @cfg {Boolean} active - * Indicates the initial status of the filter (defaults to false). - */ - active: false, - /** - * @property {String} type - * The filter type. Used by the filters.Feature class when adding filters and applying state. - */ - type: 'string', - /** - * @cfg {String} dataIndex - * The {@link Ext.data.Store} dataIndex of the field this filter represents. - * The dataIndex does not actually have to exist in the store. - */ - dataIndex: null, - /** - * @property {Ext.menu.Menu} menu - * The filter configuration menu that will be installed into the filter submenu of a column menu. - */ - menu: null, - isGridFilter: true, - defaultRoot: 'data', - /** - * The prefix for id's used to track stateful Store filters. - * @private - */ - filterIdPrefix: Ext.baseCSSPrefix + 'gridfilter', - /** - * @event activate - * Fires when an inactive filter becomes active - * @param {Ext.grid.filters.Filters} this - */ - /** - * @event deactivate - * Fires when an active filter becomes inactive - * @param {Ext.grid.filters.Filters} this - */ - /** - * @event update - * Fires when a filter configuration has changed - * @param {Ext.grid.filters.Filters} this The filter object. - */ - /** - * Initializes the filter given its configuration. - * @param {Object} config - */ - constructor: function(config) { - var me = this, - column; - me.initConfig(config); - column = me.column; - column.on('destroy', me.destroy, me); - me.dataIndex = me.dataIndex || column.dataIndex; - me.task = new Ext.util.DelayedTask(me.setValue, me); - }, - /** - * Destroys this filter by purging any event listeners, and removing any menus. - */ - destroy: function() { - this.grid = this.menu = Ext.destroy(this.menu); - this.callParent(); - }, - addStoreFilter: function(filter) { - this.getGridStore().getFilters().add(filter); - }, - createFilter: function(config, key) { - return new Ext.util.Filter(this.getFilterConfig(config, key)); - }, - // Note that some derived classes may need to do specific processing and will have its own version of this method - // before calling parent (see the List filter). - getFilterConfig: function(config, key) { - config.id = this.getBaseIdPrefix(); - if (!config.property) { - config.property = this.dataIndex; - } - if (!config.root) { - config.root = this.defaultRoot; - } - if (key) { - config.id += '-' + key; - } - return config; - }, - /** - * @private - * Creates the Menu for this filter. - * @param {Object} config Filter configuration - * @return {Ext.menu.Menu} - */ - createMenu: function() { - this.menu = Ext.widget(this.getMenuConfig()); - }, - getActiveState: function(config, value) { - // An `active` config must take precedence over a `value` config. - var active = config.active; - return (active !== undefined) ? active : value !== undefined; - }, - getBaseIdPrefix: function() { - return this.filterIdPrefix + '-' + this.dataIndex; - }, - getMenuConfig: function() { - return Ext.apply({}, this.getMenuDefaults()); - }, - getGridStore: function() { - return this.grid.getStore(); - }, - getStoreFilter: function(key) { - var id = this.getBaseIdPrefix(); - if (key) { - id += '-' + key; - } - return this.getGridStore().getFilters().get(id); - }, - /** - * @private - * Handler method called when there is a significant event on an input item. - */ - onValueChange: function(field, e) { - var me = this, - updateBuffer = me.updateBuffer; - if (!field.isFormField) { - Ext.raise('`field` should be a form field instance.'); - } - if (field.isValid()) { - if (e.getKey() === e.RETURN) { - me.menu.hide(); - return; - } - if (updateBuffer) { - me.task.delay(updateBuffer, null, null, [ - me.getValue(field) - ]); - } else { - me.setValue(me.getValue(field)); - } - } - }, - /** - * @private - * @method preprocess - * Template method to be implemented by all subclasses that need to perform - * any operations before the column filter has finished construction. - * @template - */ - preprocess: Ext.emptyFn, - removeStoreFilter: function(filter) { - this.getGridStore().getFilters().remove(filter); - }, - /** - * @private - * @method getValue - * Template method to be implemented by all subclasses that is to - * get and return the value of the filter. - * @return {Object} The 'serialized' form of this filter - * @template - */ - getValue: Ext.emptyFn, - /** - * @private - * @method setValue - * Template method to be implemented by all subclasses that is to - * set the value of the filter and fire the 'update' event. - * @param {Object} data The value to set the filter - * @template - */ - /** - * Sets the status of the filter and fires the appropriate events. - * @param {Boolean} active The new filter state. - * @param {String} key The filter key for columns that support multiple filters. - */ - setActive: function(active) { - var me = this, - menuItem = me.owner.activeFilterMenuItem, - filterCollection; - if (me.active !== active) { - me.active = active; - // The store filter will be updated, but we don't want to recreate the list store or the menu items in the - // onDataChanged listener so we need to set this flag. - me.preventDefault = true; - filterCollection = me.getGridStore().getFilters(); - filterCollection.beginUpdate(); - if (active) { - me.activate(); - } else { - me.deactivate(); - } - filterCollection.endUpdate(); - me.preventDefault = false; - // Make sure we update the 'Filters' menu item. - if (menuItem && menuItem.activeFilter === me) { - menuItem.setChecked(active); - } - me.setColumnActive(active); - } - }, - // TODO: fire activate/deactivate - setColumnActive: function(active) { - this.column[active ? 'addCls' : 'removeCls'](this.owner.filterCls); - }, - showMenu: function(menuItem) { - var me = this; - if (!me.menu) { - me.createMenu(); - } - menuItem.activeFilter = me; - menuItem.setMenu(me.menu, false); - menuItem.setChecked(me.active); - // Disable the menu if filter.disabled explicitly set to true. - menuItem.setDisabled(me.disabled === true); - me.activate(/*showingMenu*/ - true); - }, - updateStoreFilter: function() { - this.getGridStore().getFilters().notify('endupdate'); - } -}); - -/** - * This abstract base class is used by grid filters that have a single - * {@link Ext.data.Store#cfg-filters store filter}. - * @protected - */ -Ext.define('Ext.grid.filters.filter.SingleFilter', { - extend: 'Ext.grid.filters.filter.Base', - constructor: function(config) { - var me = this, - filter, value; - me.callParent([ - config - ]); - value = me.value; - filter = me.getStoreFilter(); - if (filter) { - // This filter was restored from stateful filters on the store so enforce it as active. - me.active = true; - } else { - // Once we've reached this block, we know that this grid filter doesn't have a stateful filter, so if our - // flag to begin saving future filter mutations is set we know that any configured filter must be nulled - // out or it will replace our stateful filter. - if (me.grid.stateful && me.getGridStore().saveStatefulFilters) { - value = undefined; - } - // TODO: What do we mean by value === null ? - me.active = me.getActiveState(config, value); - // Now we're acting on user configs so let's not futz with any assumed settings. - filter = me.createFilter({ - operator: me.operator, - value: value - }); - if (me.active) { - me.addStoreFilter(filter); - } - } - if (me.active) { - me.setColumnActive(true); - } - me.filter = filter; - }, - activate: function(showingMenu) { - if (showingMenu) { - this.activateMenu(); - } else { - this.addStoreFilter(this.filter); - } - }, - deactivate: function() { - this.removeStoreFilter(this.filter); - }, - getValue: function(field) { - return field.getValue(); - }, - onFilterRemove: function() { - // Filters can be removed at any time, even before a column filter's menu has been created (i.e., - // store.clearFilter()). - if (!this.menu || this.active) { - this.active = false; - } - } -}); - -/** - * The boolean grid filter allows you to create a filter selection that limits results - * to values matching true or false. The filter can be set programmatically or via - * user input with a configurable {@link Ext.form.field.Radio radio field} in the filter section - * of the column header. - * - * Boolean filters use unique radio group IDs, so you may utilize more than one. - * - * Example Boolean Filter Usage: - * - * @example - * var shows = Ext.create('Ext.data.Store', { - * fields: ['id','show', 'visible'], - * data: [ - * {id: 0, show: 'Battlestar Galactica', visible: true}, - * {id: 1, show: 'Doctor Who', visible: true}, - * {id: 2, show: 'Farscape', visible: false}, - * {id: 3, show: 'Firefly', visible: true}, - * {id: 4, show: 'Star Trek', visible: true}, - * {id: 5, show: 'Star Wars: Christmas Special', visible: false} - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * renderTo: Ext.getBody(), - * title: 'Sci-Fi Television', - * height: 250, - * width: 375, - * store: shows, - * plugins: 'gridfilters', - * columns: [{ - * dataIndex: 'id', - * text: 'ID', - * width: 50 - * },{ - * dataIndex: 'show', - * text: 'Show', - * flex: 1 - * },{ - * dataIndex: 'visible', - * text: 'Visibility', - * width: 125, - * filter: { - * type: 'boolean', - * value: 'true', - * yesText: 'True', - * noText: 'False' - * } - * }] - * }); - */ -Ext.define('Ext.grid.filters.filter.Boolean', { - extend: 'Ext.grid.filters.filter.SingleFilter', - alias: 'grid.filter.boolean', - type: 'boolean', - operator: '=', - /** - * @cfg {Boolean} defaultValue - * Set this to null if you do not want either option to be checked by default. Defaults to false. - */ - defaultValue: false, - // - /** - * @cfg {String} yesText - * Defaults to 'Yes'. - */ - yesText: 'Yes', - // - // - /** - * @cfg {String} noText - * Defaults to 'No'. - */ - noText: 'No', - // - updateBuffer: 0, - /** - * @private - * Template method that is to initialize the filter and install required menu items. - */ - createMenu: function(config) { - var me = this, - gId = Ext.id(), - listeners = { - scope: me, - click: me.onClick - }, - itemDefaults = me.getItemDefaults(); - me.callParent(arguments); - me.menu.add([ - Ext.apply({ - text: me.yesText, - filterKey: 1, - group: gId, - checked: !!me.defaultValue, - listeners: listeners - }, itemDefaults), - Ext.apply({ - text: me.noText, - filterKey: 0, - group: gId, - checked: !me.defaultValue, - listeners: listeners - }, itemDefaults) - ]); - }, - /** - * @private - */ - onClick: function(field) { - this.setValue(!!field.filterKey); - }, - /** - * @private - * Template method that is to set the value of the filter. - * @param {Object} value The value to set the filter. - */ - setValue: function(value) { - var me = this; - me.filter.setValue(value); - if (value !== undefined && me.active) { - me.value = value; - me.updateStoreFilter(); - } else { - me.setActive(true); - } - }, - // This is supposed to be just a stub. - activateMenu: Ext.emptyFn -}); - -/** - * This abstract base class is used by grid filters that have a three - * {@link Ext.data.Store#cfg-filters store filter}. - * @protected - */ -Ext.define('Ext.grid.filters.filter.TriFilter', { - extend: 'Ext.grid.filters.filter.Base', - /** - * @property {String[]} menuItems - * The items to be shown in this menu. Items are added to the menu - * according to their position within this array. - * Defaults to: - * menuItems : ['lt', 'gt', '-', 'eq'] - * @private - */ - menuItems: [ - 'lt', - 'gt', - '-', - 'eq' - ], - constructor: function(config) { - var me = this, - stateful = false, - filter = {}, - filterGt, filterLt, filterEq, value, operator; - me.callParent([ - config - ]); - value = me.value; - filterLt = me.getStoreFilter('lt'); - filterGt = me.getStoreFilter('gt'); - filterEq = me.getStoreFilter('eq'); - if (filterLt || filterGt || filterEq) { - // This filter was restored from stateful filters on the store so enforce it as active. - stateful = me.active = true; - if (filterLt) { - me.onStateRestore(filterLt); - } - if (filterGt) { - me.onStateRestore(filterGt); - } - if (filterEq) { - me.onStateRestore(filterEq); - } - } else { - // Once we've reached this block, we know that this grid filter doesn't have a stateful filter, so if our - // flag to begin saving future filter mutations is set we know that any configured filter must be nulled - // out or it will replace our stateful filter. - if (me.grid.stateful && me.getGridStore().saveStatefulFilters) { - value = undefined; - } - // TODO: What do we mean by value === null ? - me.active = me.getActiveState(config, value); - } - // Note that stateful filters will have already been gotten above. If not, or if all filters aren't stateful, we - // need to make sure that there is an actual filter instance created, with or without a value. - // - // Note use the alpha alias for the operators ('gt', 'lt', 'eq') so they map in Filters.onFilterRemove(). - filter.lt = filterLt || me.createFilter({ - operator: 'lt', - value: (!stateful && value && Ext.isDefined(value.lt)) ? value.lt : null - }, 'lt'); - filter.gt = filterGt || me.createFilter({ - operator: 'gt', - value: (!stateful && value && Ext.isDefined(value.gt)) ? value.gt : null - }, 'gt'); - filter.eq = filterEq || me.createFilter({ - operator: 'eq', - value: (!stateful && value && Ext.isDefined(value.eq)) ? value.eq : null - }, 'eq'); - me.filter = filter; - if (me.active) { - me.setColumnActive(true); - if (!stateful) { - for (operator in value) { - me.addStoreFilter(me.filter[operator]); - } - } - } - }, - // TODO: maybe call this.activate? - /** - * @private - * This method will be called when a column's menu trigger is clicked as well as when a filter is - * activated. Depending on the caller, the UI and the store will be synced. - */ - activate: function(showingMenu) { - var me = this, - filters = me.filter, - fields = me.fields, - filter, field, operator, value, isRootMenuItem; - if (me.preventFilterRemoval) { - return; - } - for (operator in filters) { - filter = filters[operator]; - field = fields[operator]; - value = filter.getValue(); - if (value || value === 0) { - field.setValue(value); - // Some types, such as Date, have additional menu check items in their Filter menu hierarchy. Others, such as Number, do not. - // Because of this, it is necessary to make sure that the direct menuitem ancestor of the fields is not the rootMenuItem (the - // "Filters" menu item), which has its checked state controlled elsewhere. - // - // In other words, if the ancestor is not the rootMenuItem, check it. - if (isRootMenuItem === undefined) { - isRootMenuItem = me.owner.activeFilterMenuItem === field.up('menuitem'); - } - if (!isRootMenuItem) { - field.up('menuitem').setChecked(true, /*suppressEvents*/ - true); - } - // Note that we only want to add store filters when they've been removed, which means that when Filter.showMenu() is called - // we DO NOT want to add a filter as they've already been added! - if (!showingMenu) { - me.addStoreFilter(filter); - } - } - } - }, - /** - * @private - * This method will be called when a filter is deactivated. The UI and the store will be synced. - */ - deactivate: function() { - var me = this, - filters = me.filter, - f, filter, value; - if (!me.countActiveFilters() || me.preventFilterRemoval) { - return; - } - me.preventFilterRemoval = true; - for (f in filters) { - filter = filters[f]; - value = filter.getValue(); - if (value || value === 0) { - me.removeStoreFilter(filter); - } - } - me.preventFilterRemoval = false; - }, - countActiveFilters: function() { - var filters = this.filter, - filterCollection = this.getGridStore().getFilters(), - prefix = this.getBaseIdPrefix(), - i = 0, - filter; - if (filterCollection.length) { - for (filter in filters) { - if (filterCollection.get(prefix + '-' + filter)) { - i++; - } - } - } - return i; - }, - onFilterRemove: function(operator) { - var me = this, - value; - // Filters can be removed at any time, even before a column filter's menu has been created (i.e., - // store.clearFilter()). So, only call setValue() if the menu has been created since that method - // assumes that menu fields exist. - if (!me.menu && me.countActiveFilters()) { - me.active = false; - } else if (me.menu) { - value = {}; - value[operator] = null; - me.setValue(value); - } - }, - onStateRestore: Ext.emptyFn, - setValue: function(value) { - var me = this, - filters = me.filter, - add = [], - remove = [], - active = false, - filterCollection = me.getGridStore().getFilters(), - field, filter, v, i, len, rLen, aLen; - if (me.preventFilterRemoval) { - return; - } - me.preventFilterRemoval = true; - if ('eq' in value) { - v = filters.lt.getValue(); - if (v || v === 0) { - remove.push(filters.lt); - } - v = filters.gt.getValue(); - if (v || v === 0) { - remove.push(filters.gt); - } - v = value.eq; - if (v || v === 0) { - add.push(filters.eq); - filters.eq.setValue(v); - } else { - remove.push(filters.eq); - } - } else { - v = filters.eq.getValue(); - if (v || v === 0) { - remove.push(filters.eq); - } - if ('lt' in value) { - v = value.lt; - if (v || v === 0) { - add.push(filters.lt); - filters.lt.setValue(v); - } else { - remove.push(filters.lt); - } - } - if ('gt' in value) { - v = value.gt; - if (v || v === 0) { - add.push(filters.gt); - filters.gt.setValue(v); - } else { - remove.push(filters.gt); - } - } - } - // Note that we don't want to update the filter collection unnecessarily, so we must know the - // current number of active filters that this TriFilter has +/- the number of filters we're - // adding and removing, respectively. This will determine the present active state of the - // TriFilter which we can use to not only help determine if the condition below should pass - // but (if it does) how the active state should then be updated. - rLen = remove.length; - aLen = add.length; - active = !!(me.countActiveFilters() + aLen - rLen); - if (rLen || aLen || active !== me.active) { - // Begin the update now because the update could also be triggered if #setActive is called. - // We must wrap all the calls that could change the filter collection. - filterCollection.beginUpdate(); - if (rLen) { - for (i = 0; i < rLen; i++) { - filter = remove[i]; - me.fields[filter.getOperator()].setValue(null); - filter.setValue(null); - me.removeStoreFilter(filter); - } - } - if (aLen) { - for (i = 0; i < aLen; i++) { - me.addStoreFilter(add[i]); - } - } - me.setActive(active); - filterCollection.endUpdate(); - } - me.preventFilterRemoval = false; - } -}); - -/** - * The date grid filter allows you to create a filter selection that limits results - * to values matching specific date constraints. The filter can be set programmatically or via - * user input with a configurable {@link Ext.picker.Date DatePicker menu} in the filter section - * of the column header. - * - * Example Date Filter Usage: - * - * @example - * var shows = Ext.create('Ext.data.Store', { - * fields: ['id','show', { - * name: 'airDate', - * type: 'date', - * dateFormat: 'Y-m-d' - * }], - * data: [ - * {id: 0, show: 'Battlestar Galactica', airDate: '1978-09-17'}, - * {id: 1, show: 'Doctor Who', airDate: '1963-11-23'}, - * {id: 2, show: 'Farscape', airDate: '1999-03-19'}, - * {id: 3, show: 'Firefly', airDate: '2002-12-20'}, - * {id: 4, show: 'Star Trek', airDate: '1966-09-08'}, - * {id: 5, show: 'Star Wars: Christmas Special', airDate: '1978-11-17'} - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * renderTo: Ext.getBody(), - * title: 'Sci-Fi Television', - * height: 250, - * width: 375, - * store: shows, - * plugins: 'gridfilters', - * columns: [{ - * dataIndex: 'id', - * text: 'ID', - * width: 50 - * },{ - * dataIndex: 'show', - * text: 'Show', - * flex: 1 - * },{ - * xtype: 'datecolumn', - * dataIndex: 'airDate', - * text: 'Original Air Date', - * width: 125, - * filter: { - * type: 'date', - * - * // optional picker config - * pickerDefaults: { - * // any DatePicker configs - * } - * } - * }] - * }); - */ -Ext.define('Ext.grid.filters.filter.Date', { - extend: 'Ext.grid.filters.filter.TriFilter', - alias: 'grid.filter.date', - uses: [ - 'Ext.picker.Date', - 'Ext.menu.DatePicker' - ], - type: 'date', - config: { - // - /** - * @cfg {Object} [fields] - * Configures field items individually. These properties override those defined - * by `{@link #itemDefaults}`. - * - * Example usage: - * fields: { - * gt: { // override fieldCfg options - * width: 200 - * } - * }, - */ - fields: { - lt: { - text: 'Before' - }, - gt: { - text: 'After' - }, - eq: { - text: 'On' - } - }, - // - /** - * @cfg {Object} pickerDefaults - * Configuration options for the date picker associated with each field. - */ - pickerDefaults: { - xtype: 'datepicker', - border: 0 - }, - updateBuffer: 0, - /** - * @cfg {String} dateFormat - * The date format to return when using getValue. - * Defaults to {@link Ext.Date#defaultFormat}. - */ - dateFormat: undefined - }, - itemDefaults: { - xtype: 'menucheckitem', - selectOnFocus: true, - width: 125, - menu: { - layout: 'auto', - plain: true - } - }, - /** - * @cfg {Date} maxDate - * Allowable date as passed to the Ext.DatePicker - * Defaults to undefined. - */ - /** - * @cfg {Date} minDate - * Allowable date as passed to the Ext.DatePicker - * Defaults to undefined. - */ - applyDateFormat: function(dateFormat) { - return dateFormat || Ext.Date.defaultFormat; - }, - /** - * @private - * Template method that is to initialize the filter and install required menu items. - */ - createMenu: function(config) { - var me = this, - listeners = { - scope: me, - checkchange: me.onCheckChange - }, - menuItems = me.menuItems, - fields, itemDefaults, pickerCfg, i, len, key, item, cfg, field; - me.callParent(arguments); - itemDefaults = me.getItemDefaults(); - fields = me.getFields(); - pickerCfg = Ext.apply({ - minDate: me.minDate, - maxDate: me.maxDate, - format: me.dateFormat, - listeners: { - scope: me, - select: me.onMenuSelect - } - }, me.getPickerDefaults()); - me.fields = {}; - for (i = 0 , len = menuItems.length; i < len; i++) { - key = menuItems[i]; - if (key !== '-') { - cfg = { - menu: { - xtype: 'datemenu', - hideOnClick: false, - pickerCfg: Ext.apply({ - itemId: key - }, pickerCfg) - } - }; - if (itemDefaults) { - Ext.merge(cfg, itemDefaults); - } - if (fields) { - Ext.merge(cfg, fields[key]); - } - item = me.menu.add(cfg); - // Date filter types need the field to be the datepicker in TriFilter.setValue(). - field = me.fields[key] = item.down('datepicker'); - field.filter = me.filter[key]; - field.filterKey = key; - item.on(listeners); - } else { - me.menu.add(key); - } - } - }, - /** - * Gets the menu picker associated with the passed field - * @param {String} item The field identifier ('lt', 'gt', 'eq') - * @return {Object} The menu picker - */ - getPicker: function(item) { - return this.fields[item]; - }, - /** - * @private - * Remove the filter from the store but don't update its value or the field UI. - */ - onCheckChange: function(field, checked) { - // Only do something if unchecked. If checked, it doesn't mean anything at this point since the column's store filter won't have - // any value (i.e., if a user checked this from an unchecked state, the corresponding field won't have a value for its filter). - var filter = field.down('datepicker').filter, - v; - // Only proceed if unchecked AND there's a filter value (i.e., there's something to do!). - if (!checked && filter.getValue()) { - // Normally we just want to remove the filter from the store, not also to null out the filter value. But, we want to call setValue() - // which will take care of unchecking the top-level menu item if it's been determined that Date* doesn't have any filters. - v = {}; - v[filter.getOperator()] = null; - this.setValue(v); - } - }, - onFilterRemove: function(operator) { - var v = {}; - v[operator] = null; - this.setValue(v); - this.fields[operator].up('menuitem').setChecked(false, /*suppressEvents*/ - true); - }, - onStateRestore: function(filter) { - filter.setSerializer(this.getSerializer()); - filter.setConvert(this.convertDateOnly); - }, - getFilterConfig: function(config, key) { - config = this.callParent([ - config, - key - ]); - config.serializer = this.getSerializer(); - config.convert = this.convertDateOnly; - return config; - }, - convertDateOnly: function(v) { - var result = null; - if (v) { - result = Ext.Date.clearTime(v, true).getTime(); - } - return result; - }, - getSerializer: function() { - var me = this; - return function(data) { - var value = data.value; - if (value) { - data.value = Ext.Date.format(value, me.getDateFormat()); - } - }; - }, - /** - * Handler for when the DatePicker for a field fires the 'select' event - * @param {Ext.picker.Date} picker - * @param {Object} date - */ - onMenuSelect: function(picker, date) { - var me = this, - fields = me.fields, - filters = me.filter, - field = fields[picker.itemId], - gt = fields.gt, - lt = fields.lt, - eq = fields.eq, - v = {}; - field.up('menuitem').setChecked(true, /*suppressEvents*/ - true); - if (field === eq) { - lt.up('menuitem').setChecked(false, true); - gt.up('menuitem').setChecked(false, true); - } else { - eq.up('menuitem').setChecked(false, true); - if (field === gt && (+lt.value < +date)) { - lt.up('menuitem').setChecked(false, true); - // Null so filter will be removed from store, but only if it currently has a value. - // The Trifilter uses the number of removed filters as one of the determinants to determine - // whether the gridfilter should be active, so don't push a value in unless it's changed. - if (filters.lt.getValue() != null) { - v.lt = null; - } - } else if (field === lt && (+gt.value > +date)) { - gt.up('menuitem').setChecked(false, true); - // Null so filter will be removed from store, but only if it currently has a value. - // The Trifilter uses the number of removed filters as one of the determinants to determine - // whether the gridfilter should be active, so don't push a value in unless it's changed. - if (filters.gt.getValue() != null) { - v.gt = null; - } - } - } - v[field.filterKey] = date; - me.setValue(v); - picker.up('menu').hide(); - } -}); - -/** - * The list grid filter allows you to create a filter selection that limits results - * to values matching an element in a list. The filter can be set programmatically or via - * user input with a configurable {@link Ext.form.field.Checkbox check box field} in the filter section - * of the column header. - * - * List filters are able to be preloaded/backed by an Ext.data.Store to load - * their options the first time they are shown. They are also able to create their own - * list of values from all unique values of the specified {@link #dataIndex} field in - * the store at first time of filter invocation. - * - * Example List Filter Usage: - * - * @example - * var shows = Ext.create('Ext.data.Store', { - * fields: ['id','show','rating'], - * data: [ - * {id: 0, show: 'Battlestar Galactica', rating: 2}, - * {id: 1, show: 'Doctor Who', rating: 4}, - * {id: 2, show: 'Farscape', rating: 3}, - * {id: 3, show: 'Firefly', rating: 4}, - * {id: 4, show: 'Star Trek', rating: 1}, - * {id: 5, show: 'Star Wars: Christmas Special', rating: 5} - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * renderTo: Ext.getBody(), - * title: 'Sci-Fi Television', - * height: 250, - * width: 350, - * store: shows, - * plugins: 'gridfilters', - * columns: [{ - * dataIndex: 'id', - * text: 'ID', - * width: 50 - * },{ - * dataIndex: 'show', - * text: 'Show', - * flex: 1 - * },{ - * dataIndex: 'rating', - * text: 'Rating', - * width: 75, - * filter: { - * type: 'list', - * value: 5 - * } - * }] - * }); - * - * ## Options - * - * There are three means to determine the list of options to present to the user: - * - * * The `{@link #cfg-options options}` config. - * * The `{@link #cfg-store store}` config. In this mode, the `{@link #cfg-idField}` - * and `{@link #cfg-labelField}` configs are used to extract the presentation and - * filtering values from the `store` and apply to the menu items and grid store - * filter, respectively. - * * If none of the above is specified, the associated grid's store is used. In this - * case, the `{@link #cfg-dataIndex}` is used to determine the filter values and - * the `{@link #cfg-labelIndex}` is used to populate the menu items. These fields - * are extracted from the records in the associated grid's store. Both of these - * configs default to the column's `dataIndex` property. - * - * In all of these modes, a store is created that is synchronized with the menu items. - * The records in this store have `{@link #cfg-idField}` and `{@link #cfg-labelField}` - * fields that get populated from which ever source was provided. - * - * var filters = Ext.create('Ext.grid.Panel', { - * ... - * columns: [{ - * text: 'Size', - * dataIndex: 'size', - * - * filter: { - * type: 'list', - * // options will be used as data to implicitly creates an ArrayStore - * options: ['extra small', 'small', 'medium', 'large', 'extra large'] - * } - * }], - * ... - * }); - */ -Ext.define('Ext.grid.filters.filter.List', { - extend: 'Ext.grid.filters.filter.SingleFilter', - alias: 'grid.filter.list', - type: 'list', - operator: 'in', - /** - * @cfg {Object} [itemDefaults] - * See the {@link Ext.grid.filters.filter.Base#cfg-itemDefaults documentation} for - * the base class for details. - * - * In the case of this class, however, note that the `checked` config should **not** be - * specified. - */ - itemDefaults: { - checked: false, - hideOnClick: false - }, - /** - * @cfg {Array} [options] - * The data to be used to implicitly create a data store to back this list. This is used only when - * the data source is **local**. If the data for the list is remote, use the {@link #store} - * config instead. - * - * If neither store nor {@link #options} is specified, then the choices list is automatically - * populated from all unique values of the specified {@link #dataIndex} field in the store at first - * time of filter invocation. - * - * Each item within the provided array may be in one of the following formats: - * - * - **Array** : - * - * options: [ - * [11, 'extra small'], - * [18, 'small'], - * [22, 'medium'], - * [35, 'large'], - * [44, 'extra large'] - * ] - * - * - **Object** : - * - * labelField: 'name', // override default of 'text' - * options: [ - * {id: 11, name:'extra small'}, - * {id: 18, name:'small'}, - * {id: 22, name:'medium'}, - * {id: 35, name:'large'}, - * {id: 44, name:'extra large'} - * ] - * - * - **String** : - * - * options: ['extra small', 'small', 'medium', 'large', 'extra large'] - * - */ - /** - * @cfg {String} [idField="id"] - * The field name for the `id` of records in this list's `{@link #cfg-store}`. These values are - * used to populate the filter for the grid's store. - */ - idField: 'id', - /** - * @cfg {String} [labelField="text"] - * The field name for the menu item text in the records in this list's `{@link #cfg-store}`. - */ - labelField: 'text', - /** - * @cfg {String} [labelIndex] - * The field in the records of the grid's store from which the menu item text should be retrieved. - * This field is only used when no `{@link #cfg-options}` and no `{@link #cfg-store}` is provided - * and the distinct value of the grid's store need to be generated dynamically. - * - * If not provided, this field defaults to the column's `dataIndex` property. - * @since 5.1.0 - */ - labelIndex: null, - // - /** - * @cfg {String} [loadingText="Loading..."] - * The text that is displayed while the configured store is loading. - */ - loadingText: 'Loading...', - // - /** - * @cfg {Boolean} loadOnShow - * Defaults to true. - */ - loadOnShow: true, - /** - * @cfg {Boolean} single - * Specify true to group all items in this list into a single-select - * radio button group. Defaults to false. - */ - single: false, - plain: true, - /** - * @cfg {Ext.data.Store} [store] - * The {@link Ext.data.Store} this list should use as its data source. - * - * If neither store nor {@link #options} is specified, then the choices list is automatically - * populated from all unique values of the specified {@link #dataIndex} field in the store at first - * time of filter invocation. - */ - constructor: function(config) { - var me = this, - gridStore; - me.callParent([ - config - ]); - if (me.itemDefaults.checked) { - Ext.raise('The itemDefaults.checked config is not supported, use the value config instead.'); - } - me.labelIndex = me.labelIndex || me.column.dataIndex; - // In order to fully support the `active` config, we need to do some preprocessing in case we need - // to fetch store data in order to create the options menu items. - // - // For instance, imagine if a list filter has the following definition: - // - // filter: { - // type: 'list', - // value: 'Bruce Springsteen' - // } - // - // Since there is no `options` or `store` config, it will need to infer its options store data from - // the grid store. Since it is also active by default if not explicitly configured as `value: false`, - // it must register listeners with the grid store now so its own column filter store will be created - // and filtered immediately and properly sync its options when the grid store changes. - // - // So here we need to subscribe to very specific events. We can't subscribe to a catch-all like - // 'datachanged' because the listener will get called too many times. This will respond to the following - // scenarios: - // 1. Removing a filter - // 2. Adding a filter - // 3. (Re)loading the store - // 4. Updating a model - // - // Note we need to make sure it's not the empty store (if it is, the store is being bound to a VM). - if (!me.options && (me.value != null) && me.active) { - gridStore = me.getGridStore(); - if (!gridStore.isEmptyStore) { - gridStore.on(me.getGridStoreListeners()); - } - me.grid.on('reconfigure', me.onReconfigure, me); - me.inferOptionsFromGridStore = true; - } - }, - destroy: function() { - var me = this, - store = me.store, - autoStore = me.autoStore, - gridStoreListeners = me.gridStoreListeners; - // We may bind listeners to both the options store & grid store, so we - // need to unbind both sets here - if (store) { - if (autoStore || store.autoDestroy) { - store.destroy(); - } else { - store.un('load', me.bindMenuStore, me); - } - me.store = null; - } - if (me.inferOptionsFromGridStore) { - me.grid.un('reconfigure', me.onReconfigure, me); - } - if (gridStoreListeners) { - me.getGridStore().un(gridStoreListeners); - me.gridStoreListeners = null; - } - me.callParent(); - }, - activateMenu: function() { - var me = this, - value = me.filter.getValue(), - items, i, len, checkItem; - if (!value || !value.length) { - return; - } - items = me.menu.items; - for (i = 0 , len = items.length; i < len; i++) { - checkItem = items.getAt(i); - if (Ext.Array.indexOf(value, checkItem.value) > -1) { - checkItem.setChecked(true, /*suppressEvents*/ - true); - } - } - }, - bindMenuStore: function(options) { - var me = this; - if (me.grid.destroyed || me.preventFilterRemoval) { - return; - } - me.createListStore(options); - me.createMenuItems(me.store); - me.loaded = true; - }, - createListStore: function(options) { - var me = this, - store = me.store, - isStore = options.isStore, - idField = me.idField, - labelField = me.labelField, - optionsStore = false, - storeData, o, i, len, value; - if (isStore) { - if (options !== me.getGridStore()) { - optionsStore = true; - store = me.store = options; - } else { - me.autoStore = true; - storeData = me.getOptionsFromStore(options); - } - } else { - storeData = []; - for (i = 0 , len = options.length; i < len; i++) { - value = options[i]; - switch (Ext.typeOf(value)) { - case 'array': - storeData.push(value); - break; - case 'object': - storeData.push(value); - break; - default: - if (value != null) { - o = {}; - o[idField] = value; - o[labelField] = value; - storeData.push(o); - }; - } - } - } - if (!optionsStore) { - if (store) { - store.destroy(); - } - store = me.store = new Ext.data.Store({ - fields: [ - idField, - labelField - ], - data: storeData - }); - // Note that the grid store listeners may have been bound in the constructor if it was determined - // that the grid filter was active and defined with a value. - if (!me.gridStoreListeners) { - me.getGridStore().on(me.getGridStoreListeners()); - } - me.loaded = true; - } - me.setStoreFilter(store); - }, - /** - * @private - * Creates the Menu for this filter. - * @param {Object} config Filter configuration - * @return {Ext.menu.Menu} - */ - createMenu: function(config) { - var me = this, - gridStore = me.getGridStore(), - store = me.store, - options = me.options, - menu; - if (store) { - me.store = store = Ext.StoreManager.lookup(store); - } - me.callParent([ - config - ]); - menu = me.menu; - if (store) { - if (!store.getCount()) { - menu.add({ - text: me.loadingText, - iconCls: Ext.baseCSSPrefix + 'mask-msg-text' - }); - // Add a listener that will auto-load the menu store if `loadOnShow` is true (the default). - // Don't bother with mon here, the menu is destroyed when we are - menu.on('show', me.show, me); - store.on('load', me.bindMenuStore, me, { - single: true - }); - } else { - me.createMenuItems(store); - } - } - // If there are supplied options, then we know the store is local. - else if (options) { - me.bindMenuStore(options); - } - // A ListMenu which is completely unconfigured acquires its store from the unique values of its field in the store. - // Note that the gridstore may have already been filtered on load if the column filter had been configured as active - // with no items checked by default. - else if (gridStore.getCount() || gridStore.isFiltered()) { - me.bindMenuStore(gridStore); - } else // If there are no records in the grid store, then we know it's async and we need to listen for its 'load' event. - { - gridStore.on('load', me.bindMenuStore, me, { - single: true - }); - } - }, - /** @private */ - createMenuItems: function(store) { - var me = this, - menu = me.menu, - len = store.getCount(), - contains = Ext.Array.contains, - listeners, itemDefaults, record, gid, idValue, idField, labelValue, labelField, i, item, processed; - // B/c we're listening to datachanged event, we need to make sure there's a menu. - if (len && menu) { - listeners = { - checkchange: me.onCheckChange, - scope: me - }; - itemDefaults = me.getItemDefaults(); - menu.suspendLayouts(); - menu.removeAll(true); - gid = me.single ? Ext.id() : null; - idField = me.idField; - labelField = me.labelField; - processed = []; - for (i = 0; i < len; i++) { - record = store.getAt(i); - idValue = record.get(idField); - labelValue = record.get(labelField); - // Only allow unique values. - if (labelValue == null || contains(processed, idValue)) { - - continue; - } - processed.push(labelValue); - // Note that the menu items will be set checked in filter#activate() if the value of the menu - // item is in the cfg.value array. - item = menu.add(Ext.apply({ - text: labelValue, - group: gid, - value: idValue, - listeners: listeners - }, itemDefaults)); - } - menu.resumeLayouts(true); - } - }, - getFilterConfig: function(config, key) { - // List filter needs to have its value set immediately or else could will fail when filtering since its - // _value would be undefined. - config.value = config.value || []; - return this.callParent([ - config, - key - ]); - }, - getGridStoreListeners: function() { - var me = this; - return me.gridStoreListeners = { - scope: me, - add: me.onDataChanged, - refresh: me.onDataChanged, - remove: me.onDataChanged, - update: me.onDataChanged - }; - }, - getOptionsFromStore: function(store) { - var me = this, - data = store.getData(), - map = {}, - ret = [], - dataIndex = me.dataIndex, - labelIndex = me.labelIndex, - items, i, length, recData, idValue, labelValue; - if (store.isFiltered() && !store.remoteFilter) { - data = data.getSource(); - } - items = data.items; - length = items.length; - for (i = 0; i < length; ++i) { - recData = items[i].data; - idValue = recData[dataIndex]; - labelValue = recData[labelIndex]; - if (labelValue === undefined) { - labelValue = idValue; - } - // TODO: allow null? - //if ((allowNull || !Ext.isEmpty(value)) && !map[strValue1]) { - if (!map[idValue]) { - map[idValue] = 1; - ret.push([ - idValue, - labelValue - ]); - } - } - return ret; - }, - onCheckChange: function() { - // Note that we don't care about the checked state here because #setValue will sort this out. - // #setValue will get the values of the currently-checked items and set its filter value from that. - var me = this, - updateBuffer = me.updateBuffer; - if (updateBuffer) { - me.task.delay(updateBuffer, null, null); - } else { - me.setValue(); - } - }, - onDataChanged: function(store) { - // If the menu item options (and the options store) are being auto-generated from the grid store, then it - // needs to know when the grid store has changed its data so it can remain in sync. - if (!this.preventDefault) { - this.bindMenuStore(store); - } - }, - onReconfigure: function(grid, store) { - // We need to listen for reconfigure not only for when the list filter has inferred its options from the - // grid store but also when the grid has a VM and is late-binding the store. - if (store) { - this.bindMenuStore(store); - } - }, - setStoreFilter: function(options) { - var me = this, - value = me.value, - filter = me.filter; - // If there are user-provided values we trust that they are valid (an empty array IS valid!). - if (value) { - if (!Ext.isArray(value)) { - value = [ - value - ]; - } - filter.setValue(value); - } - if (me.active) { - me.preventFilterRemoval = true; - me.addStoreFilter(filter); - me.preventFilterRemoval = false; - } - }, - /** - * @private - * Template method that is to set the value of the filter. - */ - setValue: function() { - var me = this, - items = me.menu.items, - value = [], - i, len, checkItem; - // The store filter will be updated, but we don't want to recreate the list store or the menu items in the - // onDataChanged listener so we need to set this flag. - me.preventDefault = true; - for (i = 0 , len = items.length; i < len; i++) { - checkItem = items.getAt(i); - if (checkItem.checked) { - value.push(checkItem.value); - } - } - me.filter.setValue(value); - len = value.length; - if (len && me.active) { - me.updateStoreFilter(); - } else { - me.setActive(!!len); - } - me.preventDefault = false; - }, - show: function() { - var store = this.store; - if (this.loadOnShow && !this.loaded && !store.hasPendingLoad()) { - store.load(); - } - } -}); - -/** - * Filter type for {@link Ext.grid.column.Number number columns}. - * - * @example - * var shows = Ext.create('Ext.data.Store', { - * fields: ['id','show'], - * data: [ - * {id: 0, show: 'Battlestar Galactica'}, - * {id: 1, show: 'Doctor Who'}, - * {id: 2, show: 'Farscape'}, - * {id: 3, show: 'Firefly'}, - * {id: 4, show: 'Star Trek'}, - * {id: 5, show: 'Star Wars: Christmas Special'} - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * renderTo: Ext.getBody(), - * title: 'Sci-Fi Television', - * height: 250, - * width: 250, - * store: shows, - * plugins: 'gridfilters', - * columns: [{ - * dataIndex: 'id', - * text: 'ID', - * width: 50, - * filter: 'number' // May also be 'numeric' - * },{ - * dataIndex: 'show', - * text: 'Show', - * flex: 1 - * }] - * }); - * - */ -Ext.define('Ext.grid.filters.filter.Number', { - extend: 'Ext.grid.filters.filter.TriFilter', - alias: [ - 'grid.filter.number', - 'grid.filter.numeric' - ], - uses: [ - 'Ext.form.field.Number' - ], - type: 'number', - config: { - /** - * @cfg {Object} [fields] - * Configures field items individually. These properties override those defined - * by `{@link #itemDefaults}`. - * - * Example usage: - * - * fields: { - * // Override itemDefaults for one field: - * gt: { - * width: 200 - * } - * - * // "lt" and "eq" fields retain all itemDefaults - * }, - */ - fields: { - gt: { - iconCls: Ext.baseCSSPrefix + 'grid-filters-gt', - margin: '0 0 3px 0' - }, - lt: { - iconCls: Ext.baseCSSPrefix + 'grid-filters-lt', - margin: '0 0 3px 0' - }, - eq: { - iconCls: Ext.baseCSSPrefix + 'grid-filters-eq', - margin: 0 - } - } - }, - // - /** - * @cfg {String} emptyText - * The empty text to show for each field. - */ - emptyText: 'Enter Number...', - // - itemDefaults: { - xtype: 'numberfield', - enableKeyEvents: true, - hideEmptyLabel: false, - labelSeparator: '', - labelWidth: 29, - selectOnFocus: false - }, - menuDefaults: { - // A menu with only form fields needs some body padding. Normally this padding - // is managed by the items, but we have no normal menu items. - bodyPadding: 3, - showSeparator: false - }, - createMenu: function() { - var me = this, - listeners = { - scope: me, - keyup: me.onValueChange, - spin: { - fn: me.onInputSpin, - buffer: 200 - }, - el: { - click: me.stopFn - } - }, - itemDefaults = me.getItemDefaults(), - menuItems = me.menuItems, - fields = me.getFields(), - field, i, len, key, item, cfg; - me.callParent(); - me.fields = {}; - for (i = 0 , len = menuItems.length; i < len; i++) { - key = menuItems[i]; - if (key !== '-') { - field = fields[key]; - cfg = { - labelClsExtra: Ext.baseCSSPrefix + 'grid-filters-icon ' + field.iconCls - }; - if (itemDefaults) { - Ext.merge(cfg, itemDefaults); - } - Ext.merge(cfg, field); - cfg.emptyText = cfg.emptyText || me.emptyText; - delete cfg.iconCls; - me.fields[key] = item = me.menu.add(cfg); - item.filter = me.filter[key]; - item.filterKey = key; - item.on(listeners); - } else { - me.menu.add(key); - } - } - }, - getValue: function(field) { - var value = {}; - value[field.filterKey] = field.getValue(); - return value; - }, - /** - * @private - * Handler method called when there is a spin event on a NumberField - * item of this menu. - */ - onInputSpin: function(field, direction) { - var value = {}; - value[field.filterKey] = field.getValue(); - this.setValue(value); - }, - stopFn: function(e) { - e.stopPropagation(); - } -}); - -/** - * The string grid filter allows you to create a filter selection that limits results - * to values matching a particular string. The filter can be set programmatically or via - * user input with a configurable {@link Ext.form.field.Text text field} in the filter section - * of the column header. - * - * Example String Filter Usage: - * - * @example - * var shows = Ext.create('Ext.data.Store', { - * fields: ['id','show'], - * data: [ - * {id: 0, show: 'Battlestar Galactica'}, - * {id: 1, show: 'Doctor Who'}, - * {id: 2, show: 'Farscape'}, - * {id: 3, show: 'Firefly'}, - * {id: 4, show: 'Star Trek'}, - * {id: 5, show: 'Star Wars: Christmas Special'} - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * renderTo: Ext.getBody(), - * title: 'Sci-Fi Television', - * height: 250, - * width: 250, - * store: shows, - * plugins: 'gridfilters', - * columns: [{ - * dataIndex: 'id', - * text: 'ID', - * width: 50 - * },{ - * dataIndex: 'show', - * text: 'Show', - * flex: 1, - * filter: { - * // required configs - * type: 'string', - * // optional configs - * value: 'star', // setting a value makes the filter active. - * itemDefaults: { - * // any Ext.form.field.Text configs accepted - * } - * } - * }] - * }); - */ -Ext.define('Ext.grid.filters.filter.String', { - extend: 'Ext.grid.filters.filter.SingleFilter', - alias: 'grid.filter.string', - type: 'string', - operator: 'like', - // - /** - * @cfg {String} emptyText - * The empty text to show for each field. - */ - emptyText: 'Enter Filter Text...', - // - itemDefaults: { - xtype: 'textfield', - enableKeyEvents: true, - hideEmptyLabel: false, - iconCls: Ext.baseCSSPrefix + 'grid-filters-find', - labelSeparator: '', - labelWidth: 29, - margin: 0, - selectOnFocus: true - }, - menuDefaults: { - // A menu with only form fields needs some body padding. Normally this padding - // is managed by the items, but we have no normal menu items. - bodyPadding: 3, - showSeparator: false - }, - /** - * @private - * Template method that is to initialize the filter and install required menu items. - */ - createMenu: function() { - var me = this, - config; - me.callParent(); - config = Ext.apply({}, me.getItemDefaults()); - if (config.iconCls && !('labelClsExtra' in config)) { - config.labelClsExtra = Ext.baseCSSPrefix + 'grid-filters-icon ' + config.iconCls; - } - delete config.iconCls; - config.emptyText = config.emptyText || me.emptyText; - me.inputItem = me.menu.add(config); - me.inputItem.on({ - scope: me, - keyup: me.onValueChange, - el: { - click: function(e) { - e.stopPropagation(); - } - } - }); - }, - /** - * @private - * Template method that is to set the value of the filter. - * @param {Object} value The value to set the filter. - */ - setValue: function(value) { - var me = this; - if (me.inputItem) { - me.inputItem.setValue(value); - } - me.filter.setValue(value); - if (value && me.active) { - me.value = value; - me.updateStoreFilter(); - } else { - me.setActive(!!value); - } - }, - activateMenu: function() { - this.inputItem.setValue(this.filter.getValue()); - } -}); - -/** - * This class is a grid {@link Ext.AbstractPlugin plugin} that adds a simple and flexible - * presentation for {@link Ext.data.AbstractStore#filters store filters}. - * - * Filters can be modified by the end-user using the grid's column header menu. Through - * this menu users can configure, enable, and disable filters for each column. - * - * # Example Usage - * - * @example - * var shows = Ext.create('Ext.data.Store', { - * fields: ['id','show'], - * data: [ - * {id: 0, show: 'Battlestar Galactica'}, - * {id: 1, show: 'Doctor Who'}, - * {id: 2, show: 'Farscape'}, - * {id: 3, show: 'Firefly'}, - * {id: 4, show: 'Star Trek'}, - * {id: 5, show: 'Star Wars: Christmas Special'} - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * renderTo: Ext.getBody(), - * title: 'Sci-Fi Television', - * height: 250, - * width: 250, - * store: shows, - * plugins: 'gridfilters', - * columns: [{ - * dataIndex: 'id', - * text: 'ID', - * width: 50 - * },{ - * dataIndex: 'show', - * text: 'Show', - * flex: 1, - * filter: { - * // required configs - * type: 'string', - * // optional configs - * value: 'star', // setting a value makes the filter active. - * itemDefaults: { - * // any Ext.form.field.Text configs accepted - * } - * } - * }] - * }); - * - * # Features - * - * ## Filtering implementations - * - * Currently provided filter types are: - * - * * `{@link Ext.grid.filters.filter.Boolean boolean}` - * * `{@link Ext.grid.filters.filter.Date date}` - * * `{@link Ext.grid.filters.filter.List list}` - * * `{@link Ext.grid.filters.filter.Number number}` - * * `{@link Ext.grid.filters.filter.String string}` - * - * **Note:** You can find inline examples for each filter on its specific filter page. - * - * ## Graphical Indicators - * - * Columns that are filtered have {@link #filterCls CSS class} applied to their column - * headers. This style can be managed using that CSS class or by setting these Sass - * variables in your theme or application: - * - * $grid-filters-column-filtered-font-style: italic !default; - * - * $grid-filters-column-filtered-font-weight: bold !default; - * - * ## Stateful - * - * Filter information will be persisted across page loads by specifying a `stateId` - * in the Grid configuration. In actuality this state is saved by the `store`, but this - * plugin ensures that saved filters are properly identified and reclaimed on subsequent - * visits to the page. - * - * ## Grid Changes - * - * - A `filters` property is added to the Grid using this plugin. - * - * # Upgrading From Ext.ux.grid.FilterFeature - * - * The biggest change for developers converting from the user extension is most likely the - * conversion to standard {@link Ext.data.AbstractStore#filters store filters}. In the - * process, the "like" and "in" operators are now supported by `{@link Ext.util.Filter}`. - * These filters and all other filters added to the store will be sent in the standard - * way (using the "filters" parameter by default). - * - * Since this plugin now uses actual store filters, the `onBeforeLoad` listener and all - * helper methods that were used to clean and build the params have been removed. The store - * will send the filters managed by this plugin along in its normal request. -*/ -Ext.define('Ext.grid.filters.Filters', { - extend: 'Ext.plugin.Abstract', - requires: [ - 'Ext.grid.filters.filter.*' - ], - mixins: [ - 'Ext.util.StoreHolder' - ], - alias: 'plugin.gridfilters', - pluginId: 'gridfilters', - /** - * @property {Object} defaultFilterTypes - * This property maps {@link Ext.data.Model#cfg-fields field type} to the - * appropriate grid filter type. - * @private - */ - defaultFilterTypes: { - 'boolean': 'boolean', - 'int': 'number', - date: 'date', - number: 'number' - }, - /** - * @property {String} [filterCls="x-grid-filters-filtered-column"] - * The CSS applied to column headers with active filters. - */ - filterCls: Ext.baseCSSPrefix + 'grid-filters-filtered-column', - // - /** - * @cfg {String} [menuFilterText="Filters"] - * The text for the filters menu. - */ - menuFilterText: 'Filters', - // - /** - * @cfg {Boolean} showMenu - * Defaults to true, including a filter submenu in the default header menu. - */ - showMenu: true, - /** - * @cfg {String} stateId - * Name of the value to be used to store state information. - */ - stateId: undefined, - init: function(grid) { - var me = this, - store, headerCt; - Ext.Assert.falsey(me.grid); - me.grid = grid; - grid.filters = me; - if (me.grid.normalGrid) { - me.isLocked = true; - } - grid.clearFilters = me.clearFilters.bind(me); - store = grid.store; - headerCt = grid.headerCt; - headerCt.on({ - scope: me, - add: me.onAdd, - menucreate: me.onMenuCreate - }); - grid.on({ - scope: me, - destroy: me.onGridDestroy, - reconfigure: me.onReconfigure - }); - me.bindStore(store); - if (grid.stateful) { - store.statefulFilters = true; - } - me.initColumns(); - }, - /** - * Creates the Filter objects for the current configuration. - * Reconfigure and on add handlers. - * @private - */ - initColumns: function() { - var grid = this.grid, - store = grid.getStore(), - columns = grid.columnManager.getColumns(), - len = columns.length, - i, column, filter, filterCollection; - // We start with filters defined on any columns. - for (i = 0; i < len; i++) { - column = columns[i]; - filter = column.filter; - if (filter && !filter.isGridFilter) { - if (!filterCollection) { - filterCollection = store.getFilters(); - filterCollection.beginUpdate(); - } - this.createColumnFilter(column); - } - } - if (filterCollection) { - filterCollection.endUpdate(); - } - }, - createColumnFilter: function(column) { - var me = this, - columnFilter = column.filter, - filter = { - column: column, - grid: me.grid, - owner: me - }, - field, model, type; - if (Ext.isString(columnFilter)) { - filter.type = columnFilter; - } else { - Ext.apply(filter, columnFilter); - } - if (!filter.type) { - model = me.store.getModel(); - // If no filter type given, first try to get it from the data field. - field = model && model.getField(column.dataIndex); - type = field && field.type; - filter.type = (type && me.defaultFilterTypes[type]) || column.defaultFilterType || 'string'; - } - column.filter = Ext.Factory.gridFilter(filter); - }, - onAdd: function(headerCt, column, index) { - var filter = column.filter; - if (filter && !filter.isGridFilter) { - this.createColumnFilter(column); - } - }, - /** - * @private - * Handle creation of the grid's header menu. - */ - onMenuCreate: function(headerCt, menu) { - menu.on({ - beforeshow: this.onMenuBeforeShow, - scope: this - }); - }, - /** - * @private - * Handle showing of the grid's header menu. Sets up the filter item and menu - * appropriate for the target column. - */ - onMenuBeforeShow: function(menu) { - var me = this, - menuItem, filter, parentTable, parentTableId; - if (me.showMenu) { - // In the case of a locked grid, we need to cache the 'Filters' menuItem for each grid since - // there's only one Filters instance. Both grids/menus can't share the same menuItem! - if (!me.filterMenuItem) { - me.filterMenuItem = {}; - } - // Don't get the owner panel if in a locking grid since we need to get the unique filterMenuItem key. - // Instead, get a ref to the parent, i.e., lockedGrid, normalGrid, etc. - parentTable = menu.up('tablepanel'); - parentTableId = parentTable.id; - menuItem = me.filterMenuItem[parentTableId]; - if (!menuItem || menuItem.destroyed) { - menuItem = me.createMenuItem(menu, parentTableId); - } - // Save a ref to the root "Filters" menu item, column filters make use of it. - me.activeFilterMenuItem = menuItem; - filter = me.getMenuFilter(parentTable.headerCt); - if (filter) { - filter.showMenu(menuItem); - } - menuItem.setVisible(!!filter); - me.sep.setVisible(!!filter); - } - }, - createMenuItem: function(menu, parentTableId) { - var me = this, - item; - me.sep = menu.add('-'); - item = menu.add({ - checked: false, - itemId: 'filters', - text: me.menuFilterText, - listeners: { - scope: me, - checkchange: me.onCheckChange - } - }); - return (me.filterMenuItem[parentTableId] = item); - }, - /** - * Handler called by the grid 'beforedestroy' event - */ - onGridDestroy: function() { - var me = this, - filterMenuItem = me.filterMenuItem, - item; - me.bindStore(null); - me.sep = Ext.destroy(me.sep); - for (item in filterMenuItem) { - filterMenuItem[item].destroy(); - } - me.grid = null; - }, - onUnbindStore: function(store) { - store.getFilters().un('remove', this.onFilterRemove, this); - }, - onBindStore: function(store, initial, propName) { - this.local = !store.getRemoteFilter(); - store.getFilters().on('remove', this.onFilterRemove, this); - }, - onFilterRemove: function(filterCollection, list) { - // We need to know when a store filter has been removed by an operation of the gridfilters UI, i.e., - // store.clearFilter(). The preventFilterRemoval flag lets us know whether or not this listener has been - // reached by a filter operation (preventFilterRemoval === true) or by something outside of the UI - // (preventFilterRemoval === undefined). - var len = list.items.length, - columnManager = this.grid.columnManager, - i, item, header, filter; - for (i = 0; i < len; i++) { - item = list.items[i]; - header = columnManager.getHeaderByDataIndex(item.getProperty()); - if (header) { - // First, we need to make sure there is indeed a filter and that its menu has been created. If not, - // there's no point in continuing. - // - // Also, even though the store may be filtered by this dataIndex, it doesn't necessarily mean that - // it was created via the gridfilters API. To be sure, we need to check the prefix, as this is the - // only way we can be sure of its provenance (note that we can't check `operator`). - // - // Note that we need to do an indexOf check on the string because TriFilters will contain extra - // characters specifying its type. - // - // TODO: Should we support updating the gridfilters if one or more of its filters have been removed - // directly by the bound store? - filter = header.filter; - if (!filter || !filter.menu || item.getId().indexOf(filter.getBaseIdPrefix()) === -1) { - - continue; - } - if (!filter.preventFilterRemoval) { - // This is only called on the filter if called from outside of the gridfilters UI. - filter.onFilterRemove(item.getOperator()); - } - } - } - }, - /** - * @private - * Get the filter menu from the filters MixedCollection based on the clicked header. - */ - getMenuFilter: function(headerCt) { - return headerCt.getMenu().activeHeader.filter; - }, - /** @private */ - onCheckChange: function(item, value) { - // Locking grids must lookup the correct grid. - var parentTable = this.isLocked ? item.up('tablepanel') : this.grid, - filter = this.getMenuFilter(parentTable.headerCt); - filter.setActive(value); - }, - getHeaders: function() { - return this.grid.view.headerCt.columnManager.getColumns(); - }, - /** - * Checks the plugin's grid for statefulness. - * @return {Boolean} - */ - isStateful: function() { - return this.grid.stateful; - }, - /** - * Adds a filter to the collection and creates a store filter if has a `value` property. - * @param {Object/Object[]/Ext.util.Filter/Ext.util.Filter[]} filters A filter - * configuration or a filter object. - */ - addFilter: function(filters) { - var me = this, - grid = me.grid, - store = me.store, - hasNewColumns = false, - suppressNextFilter = true, - dataIndex, column, i, len, filter, columnFilter; - if (!Ext.isArray(filters)) { - filters = [ - filters - ]; - } - for (i = 0 , len = filters.length; i < len; i++) { - filter = filters[i]; - dataIndex = filter.dataIndex; - column = grid.columnManager.getHeaderByDataIndex(dataIndex); - // We only create filters that map to an existing column. - if (column) { - hasNewColumns = true; - // Don't suppress active filters. - if (filter.value) { - suppressNextFilter = false; - } - columnFilter = column.filter; - // If already a gridfilter, let's destroy it and recreate another from the new config. - if (columnFilter && columnFilter.isGridFilter) { - columnFilter.deactivate(); - columnFilter.destroy(); - if (me.activeFilterMenuItem) { - me.activeFilterMenuItem.menu = null; - } - } - column.filter = filter; - } - } - // Batch initialize all column filters. - if (hasNewColumns) { - store.suppressNextFilter = suppressNextFilter; - me.initColumns(); - store.suppressNextFilter = false; - } - }, - /** - * Adds filters to the collection. - * @param {Array} filters An Array of filter configuration objects. - */ - addFilters: function(filters) { - if (filters) { - this.addFilter(filters); - } - }, - /** - * Turns all filters off. This does not clear the configuration information. - */ - clearFilters: function() { - var grid = this.grid, - columns = grid.columnManager.getColumns(), - store = grid.store, - column, filter, i, len, filterCollection; - // We start with filters defined on any columns. - for (i = 0 , len = columns.length; i < len; i++) { - column = columns[i]; - filter = column.filter; - if (filter && filter.isGridFilter) { - if (!filterCollection) { - filterCollection = store.getFilters(); - filterCollection.beginUpdate(); - } - filter.setActive(false); - } - } - if (filterCollection) { - filterCollection.endUpdate(); - } - }, - onReconfigure: function(grid, store, columns, oldStore) { - var filterMenuItem = this.filterMenuItem, - key; - // The Filters item's menu should have already been destroyed by the time we get here but - // we still need to null out the menu reference. - for (key in filterMenuItem) { - filterMenuItem[key].setMenu(null); - } - if (store && oldStore !== store) { - this.bindStore(store); - } - } -}); - -/** - * Private class which acts as a HeaderContainer for the Lockable which aggregates all columns - * from both sides of the Lockable. It is never rendered, it's just used to interrogate the - * column collection. - * @private - */ -Ext.define('Ext.grid.locking.HeaderContainer', { - extend: 'Ext.grid.header.Container', - requires: [ - 'Ext.grid.ColumnManager' - ], - headerCtRelayEvents: [ - "blur", - "focus", - "move", - "resize", - "destroy", - "beforedestroy", - "boxready", - "afterrender", - "render", - "beforerender", - "removed", - "hide", - "beforehide", - "show", - "beforeshow", - "enable", - "disable", - "added", - "deactivate", - "beforedeactivate", - "activate", - "beforeactivate", - "remove", - "add", - "beforeremove", - "beforeadd", - "afterlayout", - "menucreate", - "sortchange", - "columnschanged", - "columnshow", - "columnhide", - "columnmove", - "headertriggerclick", - "headercontextmenu", - "headerclick", - "columnresize", - "statesave", - "beforestatesave", - "staterestore", - "beforestaterestore" - ], - constructor: function(lockable) { - var me = this, - lockedGrid = lockable.lockedGrid, - normalGrid = lockable.normalGrid; - me.lockable = lockable; - me.callParent(); - // Create the unified column manager for the lockable grid assembly - lockedGrid.visibleColumnManager.rootColumns = normalGrid.visibleColumnManager.rootColumns = lockable.visibleColumnManager = me.visibleColumnManager = new Ext.grid.ColumnManager(true, lockedGrid.headerCt, normalGrid.headerCt); - lockedGrid.columnManager.rootColumns = normalGrid.columnManager.rootColumns = lockable.columnManager = me.columnManager = new Ext.grid.ColumnManager(false, lockedGrid.headerCt, normalGrid.headerCt); - // Relay *all* events from the two HeaderContainers - me.lockedEventRelayers = me.relayEvents(lockedGrid.headerCt, me.headerCtRelayEvents); - me.normalEventRelayers = me.relayEvents(normalGrid.headerCt, me.headerCtRelayEvents); - }, - destroy: function() { - var me = this; - Ext.destroy(me.lockedEventRelayers, me.normalEventRelayers); - me.lockedEventRelayers = me.normalEventRelayers = null; - me.callParent(); - }, - getRefItems: function() { - return this.lockable.lockedGrid.headerCt.getRefItems().concat(this.lockable.normalGrid.headerCt.getRefItems()); - }, - // This is the function which all other column access methods are based upon - // Return the full column set for the whole Lockable assembly - getGridColumns: function() { - return this.lockable.lockedGrid.headerCt.getGridColumns().concat(this.lockable.normalGrid.headerCt.getGridColumns()); - }, - // Lockable uses its headerCt to gather column state - getColumnsState: function() { - var me = this, - locked = me.lockable.lockedGrid.headerCt.getColumnsState(), - normal = me.lockable.normalGrid.headerCt.getColumnsState(); - return locked.concat(normal); - }, - // Lockable uses its headerCt to apply column state - applyColumnsState: function(columns, storeState) { - var me = this, - lockedGrid = me.lockable.lockedGrid, - lockedHeaderCt = lockedGrid.headerCt, - normalHeaderCt = me.lockable.normalGrid.headerCt, - lockedCols = Ext.Array.toValueMap(lockedHeaderCt.items.items, 'stateId'), - normalCols = Ext.Array.toValueMap(normalHeaderCt.items.items, 'stateId'), - locked = [], - normal = [], - lockedWidth = 1, - length = columns.length, - i, existing, lockedDefault, col; - for (i = 0; i < length; i++) { - col = columns[i]; - lockedDefault = lockedCols[col.id]; - existing = lockedDefault || normalCols[col.id]; - if (existing) { - if (existing.applyColumnState) { - existing.applyColumnState(col, storeState); - } - if (existing.locked === undefined) { - existing.locked = !!lockedDefault; - } - if (existing.locked) { - locked.push(existing); - if (!existing.hidden && typeof existing.width === 'number') { - lockedWidth += existing.width; - } - } else { - normal.push(existing); - } - } - } - // state and config must have the same columns (compare counts for now): - if (locked.length + normal.length === lockedHeaderCt.items.getCount() + normalHeaderCt.items.getCount()) { - lockedHeaderCt.removeAll(false); - normalHeaderCt.removeAll(false); - lockedHeaderCt.add(locked); - normalHeaderCt.add(normal); - lockedGrid.setWidth(lockedWidth); - } - }, - disable: function() { - var topGrid = this.lockable; - topGrid.lockedGrid.headerCt.disable(); - topGrid.normalGrid.headerCt.disable(); - }, - enable: function() { - var topGrid = this.lockable; - topGrid.lockedGrid.headerCt.enable(); - topGrid.normalGrid.headerCt.enable(); - } -}); - -/** - * This class is used internally to provide a single interface when using - * a locking grid. Internally, the locking grid creates two separate grids, - * so this class is used to map calls appropriately. - * @private - */ -Ext.define('Ext.grid.locking.View', { - alternateClassName: 'Ext.grid.LockingView', - requires: [ - 'Ext.view.AbstractView', - 'Ext.view.Table' - ], - mixins: [ - 'Ext.util.Observable', - 'Ext.util.StoreHolder', - 'Ext.util.Focusable' - ], - /** - * @property {Boolean} isLockingView - * `true` in this class to identify an object as an instantiated LockingView, or subclass thereof. - */ - isLockingView: true, - loadMask: true, - eventRelayRe: /^(beforeitem|beforecontainer|item|container|cell|refresh)/, - constructor: function(config) { - var ext = Ext, - me = this, - lockedView, normalView; - me.ownerGrid = config.ownerGrid; - me.ownerGrid.view = me; - // A single NavigationModel is configured into both views. - me.navigationModel = config.locked.xtype === 'treepanel' ? new ext.tree.NavigationModel(me) : new ext.grid.NavigationModel(me); - // Disable store binding for the two child views. - // The store is bound to the *this* locking View. - // This avoids the store being bound to two views (with duplicated layouts on each store mutation) - // and also avoids the store being bound to the selection model twice. - config.locked.viewConfig.bindStore = ext.emptyFn; - config.normal.viewConfig.bindStore = me.subViewBindStore; - config.normal.viewConfig.isNormalView = config.locked.viewConfig.isLockedView = true; - // Override the point at which first refresh is kicked off. - // The initial refresh of both sides must take place within a layout suspension - // to coalescse the resulting layouts into one. - config.locked.viewConfig.beforeLayout = config.normal.viewConfig.beforeLayout = me.beforeLayout; - // Share the same NavigationModel - config.locked.viewConfig.navigationModel = config.normal.viewConfig.navigationModel = me.navigationModel; - me.lockedGrid = me.ownerGrid.lockedGrid = ext.ComponentManager.create(config.locked); - me.lockedView = lockedView = me.lockedGrid.getView(); - // The normal view uses the same selection model - me.selModel = config.normal.viewConfig.selModel = lockedView.getSelectionModel(); - if (me.lockedGrid.isTree) { - // Tree must not animate because the partner grid is unable to animate - me.lockedView.animate = false; - // When this is a locked tree, the normal side is just a gridpanel, so needs the flat NodeStore - config.normal.store = lockedView.store; - // Match configs between sides - config.normal.viewConfig.stripeRows = me.lockedView.stripeRows; - config.normal.rowLines = me.lockedGrid.rowLines; - } - // Set up a bidirectional relationship between the two sides of the locked view. - // Inject lockingGrid and normalGrid into owning panel. - // This is because during constraction, it must be possible for descendant components - // to navigate up to the owning lockable panel and then down into either side. - me.normalGrid = me.ownerGrid.normalGrid = ext.ComponentManager.create(config.normal); - lockedView.lockingPartner = normalView = me.normalView = me.normalGrid.getView(); - normalView.lockingPartner = lockedView; - me.loadMask = (config.loadMask !== undefined) ? config.loadMask : me.loadMask; - me.mixins.observable.constructor.call(me); - // Relay both view's events. - me.lockedViewEventRelayers = me.relayEvents(lockedView, ext.view.Table.events); - // Relay extra events from only the normal view. - // These are events that both sides fire (selection events), so avoid firing them twice. - me.normalViewEventRelayers = me.relayEvents(normalView, ext.view.Table.events.concat(ext.view.Table.normalSideEvents)); - normalView.on({ - scope: me, - itemmouseleave: me.onItemMouseLeave, - itemmouseenter: me.onItemMouseEnter - }); - lockedView.on({ - scope: me, - itemmouseleave: me.onItemMouseLeave, - itemmouseenter: me.onItemMouseEnter - }); - me.ownerGrid.on({ - render: me.onPanelRender, - scope: me - }); - me.loadingText = normalView.loadingText; - me.loadingCls = normalView.loadingCls; - me.loadingUseMsg = normalView.loadingUseMsg; - me.itemSelector = me.getItemSelector(); - // Share the items arrey with the normal view. - // Certain methods need access to the start/end/count - me.all = normalView.all; - // Bind to the data source. Cache it by the property name "dataSource". - // The store property is public and must reference the provided store. - // We relay each call into both normal and locked views bracketed by a layout suspension. - me.bindStore(normalView.dataSource, true, 'dataSource'); - }, - // This is injected into the two child views as the bindStore implementation. - // Subviews in a lockable asseembly do not bind to stores. - subViewBindStore: function(store) { - var me = this, - selModel; - if (me.destroying || me.destroyed) { - return; - } - selModel = me.getSelectionModel(); - selModel.bindStore(store); - selModel.bindComponent(me); - }, - // Called in the context of a child view when the first child view begins its layout run - beforeLayout: function() { - // Access the Lockable object - var me = this.ownerCt.ownerLockable.view, - lockedView = me.lockedGrid.view, - normalView = me.normalGrid.view; - if (!me.relayingOperation) { - // Perform the first refresh just before the first layout - // Locked grid may be hidden if it began with no columns, so do not refresh it, - if (me.lockedGrid.isVisible()) { - if (lockedView.refreshNeeded) { - lockedView.doFirstRefresh(lockedView.dataSource); - } - } - if (normalView.refreshNeeded) { - normalView.doFirstRefresh(normalView.dataSource); - } - } - }, - onPanelRender: function() { - var me = this, - mask = me.loadMask, - cfg = { - target: me.ownerGrid, - msg: me.loadingText, - msgCls: me.loadingCls, - useMsg: me.loadingUseMsg, - store: me.ownerGrid.store - }; - // Because this is used as a View, it should have an el. Use the owning Lockable's body. - // It also has to fire a render event so that Editing plugins can attach listeners - me.el = me.ownerGrid.getTargetEl(); - me.rendered = true; - me.initFocusableEvents(); - me.fireEvent('render', me); - if (mask) { - // either a config object - if (Ext.isObject(mask)) { - cfg = Ext.apply(cfg, mask); - } - // Attach the LoadMask to a *Component* so that it can be sensitive to resizing during long loads. - // If this DataView is floating, then mask this DataView. - // Otherwise, mask its owning Container (or this, if there *is* no owning Container). - // LoadMask captures the element upon render. - me.loadMask = new Ext.LoadMask(cfg); - } - }, - getRefOwner: function() { - return this.ownerGrid; - }, - // Implement the same API as Ext.view.Table. - // This will return the topmost, unified visible column manager - getVisibleColumnManager: function() { - // ownerGrid refers to the topmost responsible Ext.panel.Grid. - // This could be this view's ownerCt, or if part of a locking arrangement, the locking grid - return this.ownerGrid.getVisibleColumnManager(); - }, - getTopLevelVisibleColumnManager: function() { - // ownerGrid refers to the topmost responsible Ext.panel.Grid. - // This could be this view's ownerCt, or if part of a locking arrangement, the locking grid - return this.ownerGrid.getVisibleColumnManager(); - }, - getGridColumns: function() { - return this.getVisibleColumnManager().getColumns(); - }, - getEl: function(column) { - return this.getViewForColumn(column).getEl(); - }, - getCellSelector: function() { - return this.normalView.getCellSelector(); - }, - getItemSelector: function() { - return this.normalView.getItemSelector(); - }, - getViewForColumn: function(column) { - var view = this.lockedView, - inLocked; - view.headerCt.cascade(function(col) { - if (col === column) { - inLocked = true; - return false; - } - }); - return inLocked ? view : this.normalView; - }, - onItemMouseEnter: function(view, record) { - var me = this, - locked = me.lockedView, - other = me.normalView, - item; - if (view.trackOver) { - if (view !== locked) { - other = locked; - } - item = other.getNode(record); - other.highlightItem(item); - } - }, - onItemMouseLeave: function(view, record) { - var me = this, - locked = me.lockedView, - other = me.normalView; - if (view.trackOver) { - if (view !== locked) { - other = locked; - } - other.clearHighlight(); - } - }, - relayFn: function(name, args) { - args = args || []; - var me = this, - view = me.lockedView; - // Flag that we are already manipulating the view pair, so resulting excursions - // back into this class can avoid breaking the sequence. - me.relayingOperation = true; - view[name].apply(view, args); - view = me.normalView; - view[name].apply(view, args); - me.relayingOperation = false; - }, - getSelectionModel: function() { - return this.normalView.getSelectionModel(); - }, - getNavigationModel: function() { - return this.navigationModel; - }, - getStore: function() { - return this.ownerGrid.store; - }, - /** - * Changes the data store bound to this view and refreshes it. - * @param {Ext.data.Store} store The store to bind to this view - * @since 3.4.0 - */ - onBindStore: function(store, initial, propName) { - var me = this, - lockedView = me.lockedView, - normalView = me.normalView; - // If we have already achieved our first layout, refresh immediately. - // If we have bound to the Store before the first layout, then onBoxReady will - // call doFirstRefresh - if (normalView.componentLayoutCounter && !(lockedView.blockRefresh && normalView.blockRefresh)) { - Ext.suspendLayouts(); - lockedView.doFirstRefresh(store); - normalView.doFirstRefresh(store); - Ext.resumeLayouts(true); - } - }, - getStoreListeners: function() { - var me = this; - return { - // Give view listeners the highest priority, since they need to relay things to - // children first - priority: 1000, - refresh: me.onDataRefresh, - replace: me.onReplace, - add: me.onAdd, - remove: me.onRemove, - update: me.onUpdate, - clear: me.onDataRefresh, - beginupdate: me.onBeginUpdate, - endupdate: me.onEndUpdate - }; - }, - onBeginUpdate: function() { - Ext.suspendLayouts(); - this.relayFn('onBeginUpdate', arguments); - Ext.resumeLayouts(true); - }, - onEndUpdate: function() { - Ext.suspendLayouts(); - this.relayFn('onEndUpdate', arguments); - Ext.resumeLayouts(true); - }, - onDataRefresh: function() { - Ext.suspendLayouts(); - this.relayFn('onDataRefresh', arguments); - Ext.resumeLayouts(true); - }, - onReplace: function() { - Ext.suspendLayouts(); - this.relayFn('onReplace', arguments); - Ext.resumeLayouts(true); - }, - onAdd: function() { - Ext.suspendLayouts(); - this.relayFn('onAdd', arguments); - Ext.resumeLayouts(true); - }, - onRemove: function() { - Ext.suspendLayouts(); - this.relayFn('onRemove', arguments); - Ext.resumeLayouts(true); - }, - /** - * Toggles ARIA actionable mode on/off - * @param {Boolean} enabled - * @return {Boolean} Returns `false` if the request failed. - * @private - */ - setActionableMode: function(enabled, position) { - var result, targetView; - if (enabled) { - if (!position) { - position = this.getNavigationModel().getPosition(); - } - if (position) { - position = position.clone(); - // Drill down to the side that we're actioning - position.view = targetView = position.column.getView(); - // Attempt to switch the focused view to actionable. - result = targetView.setActionableMode(enabled, position); - // If successful, and the partner is visible, switch that too. - if (result !== false && targetView.lockingPartner.grid.isVisible()) { - targetView.lockingPartner.setActionableMode(enabled, position); - // If the partner side refused to cooperate, the whole locking.View must not enter actionable mode - if (!targetView.lockingPartner.actionableMode) { - targetView.setActionableMode(false); - result = false; - } - } - return result; - } else { - return false; - } - } else { - this.relayFn('setActionableMode', [ - false - ]); - } - }, - onUpdate: function() { - Ext.suspendLayouts(); - this.relayFn('onUpdate', arguments); - Ext.resumeLayouts(true); - }, - refresh: function() { - Ext.suspendLayouts(); - this.relayFn('refresh', arguments); - Ext.resumeLayouts(true); - }, - refreshView: function() { - Ext.suspendLayouts(); - this.relayFn('refreshView', arguments); - Ext.resumeLayouts(true); - }, - getNode: function(nodeInfo) { - // default to the normal view - return this.normalView.getNode(nodeInfo); - }, - getRow: function(nodeInfo) { - // default to the normal view - return this.normalView.getRow(nodeInfo); - }, - getCell: function(record, column) { - var view = this.getViewForColumn(column), - row = view.getRow(record); - return Ext.fly(row).down(column.getCellSelector()); - }, - indexOf: function(record) { - var result = this.lockedView.indexOf(record); - if (!result) { - result = this.normalView.indexOf(record); - } - return result; - }, - focus: function() { - // Delegate to the view of first visible child tablepanel of the owning lockable assembly. - var target = this.ownerGrid.down('>tablepanel:not(hidden)>tableview'); - if (target) { - target.focus(); - } - }, - focusRow: function(row) { - var view, - // Access lastFocused directly because getter nulls it if the record is no longer in view - // and all we are interested in is the lastFocused View. - lastFocused = this.getNavigationModel().lastFocused; - view = lastFocused ? lastFocused.view : this.normalView; - view.focusRow(row); - }, - focusCell: function(position) { - position.view.focusCell(position); - }, - onRowFocus: function() { - this.relayFn('onRowFocus', arguments); - }, - isVisible: function(deep) { - return this.ownerGrid.isVisible(deep); - }, - getFocusEl: function() { - var view, - // Access lastFocused directly because getter nulls it if the record is no longer in view - // and all we are interested in is the lastFocused View. - lastFocused = this.getNavigationModel().lastFocused; - view = lastFocused ? lastFocused.view : this.normalView; - return view.getFocusEl(); - }, - // Old API. Used by tests now to test coercion of navigation from hidden column to closest visible. - // Position.column includes all columns including hidden ones. - getCellInclusive: function(pos, returnDom) { - var col = pos.column, - lockedSize = this.lockedGrid.getColumnManager().getColumns().length; - // Normalize view - if (col >= lockedSize) { - // Make a copy so we don't mutate the passed object - pos = Ext.apply({}, pos); - pos.column -= lockedSize; - return this.normalView.getCellInclusive(pos, returnDom); - } else { - return this.lockedView.getCellInclusive(pos, returnDom); - } - }, - getHeaderByCell: function(cell) { - if (cell) { - return this.getVisibleColumnManager().getHeaderById(cell.getAttribute('data-columnId')); - } - return false; - }, - onRowSelect: function() { - this.relayFn('onRowSelect', arguments); - }, - onRowDeselect: function() { - this.relayFn('onRowDeselect', arguments); - }, - onCellSelect: function(cellContext) { - // Pass a contextless cell descriptor to the child view - cellContext.column.getView().onCellSelect({ - record: cellContext.record, - column: cellContext.column - }); - }, - onCellDeselect: function(cellContext) { - // Pass a contextless cell descriptor to the child view - cellContext.column.getView().onCellDeselect({ - record: cellContext.record, - column: cellContext.column - }); - }, - getCellByPosition: function(pos, returnDom) { - var me = this, - view = pos.view, - col = pos.column; - // Access the real Ext.view.Table for the specified Column - if (view === me) { - pos = new Ext.grid.CellContext(col.getView()).setPosition(pos.record, pos.column); - } - return view.getCellByPosition(pos, returnDom); - }, - getRecord: function(node) { - var result = this.lockedView.getRecord(node); - if (!result) { - result = this.normalView.getRecord(node); - } - return result; - }, - scrollBy: function() { - var normal = this.normalView; - normal.scrollBy.apply(normal, arguments); - }, - ensureVisible: function() { - var normal = this.normalView; - normal.ensureVisible.apply(normal, arguments); - }, - disable: function() { - this.relayFn('disable', arguments); - }, - enable: function() { - this.relayFn('enable', arguments); - }, - addElListener: function() { - this.relayFn('addElListener', arguments); - }, - refreshNode: function() { - this.relayFn('refreshNode', arguments); - }, - addRowCls: function() { - this.relayFn('addRowCls', arguments); - }, - removeRowCls: function() { - this.relayFn('removeRowCls', arguments); - }, - destroy: function() { - var me = this; - me.rendered = false; - // Unbind from the dataSource we bound to in constructor - me.bindStore(null, false, 'dataSource'); - Ext.destroy(me.lockedViewEventRelayers, me.normalViewEventRelayers); - me.lockedViewEventRelayers = me.normalViewEventRelayers = null; - me.callParent(); - Ext.destroy(me.loadMask, me.navigationModel, me.selModel); - me.lockedView.lockingPartner = me.normalView.lockingPartner = null; - me.lockedGrid = me.lockedView = me.normalGrid = me.normalView = null; - me.loadMask = me.navigationModel = me.selModel = me.headerCt = null; - me.ownerGrid = me.storeListeners = null; - } -}, function() { - this.borrow(Ext.Component, [ - 'up' - ]); - this.borrow(Ext.view.AbstractView, [ - 'doFirstRefresh', - 'applyFirstRefresh' - ]); - this.borrow(Ext.view.Table, [ - 'cellSelector', - 'selectedCellCls', - 'selectedItemCls' - ]); -}); - -/** - * @private - * - * Lockable is a private mixin which injects lockable behavior into any - * TablePanel subclass such as GridPanel or TreePanel. TablePanel will - * automatically inject the Ext.grid.locking.Lockable mixin in when one of the - * these conditions are met: - * - * - The TablePanel has the lockable configuration set to true - * - One of the columns in the TablePanel has locked set to true/false - * - * Each TablePanel subclass must register an alias. It should have an array - * of configurations to copy to the 2 separate tablepanels that will be generated - * to note what configurations should be copied. These are named normalCfgCopy and - * lockedCfgCopy respectively. - * - * Configurations which are specified in this class will be available on any grid or - * tree which is using the lockable functionality. - * - * By default the two grids, "locked" and "normal" will be arranged using an {@link Ext.layout.container.HBox hbox} - * layout. If the lockable grid is configured with `{@link #split split:true}`, a vertical splitter - * will be placed between the two grids to resize them. - * - * It is possible to override the layout of the lockable grid, or example, you may wish to - * use a border layout and have one of the grids collapsible. - */ -Ext.define('Ext.grid.locking.Lockable', { - alternateClassName: 'Ext.grid.Lockable', - requires: [ - 'Ext.grid.locking.View', - 'Ext.grid.header.Container', - 'Ext.grid.locking.HeaderContainer', - 'Ext.view.Table' - ], - /** - * @cfg {Boolean} syncRowHeight Synchronize rowHeight between the normal and - * locked grid view. This is turned on by default. If your grid is guaranteed - * to have rows of all the same height, you should set this to false to - * optimize performance. - */ - syncRowHeight: true, - /** - * @cfg {String} subGridXType The xtype of the subgrid to specify. If this is - * not specified lockable will determine the subgrid xtype to create by the - * following rule. Use the superclasses xtype if the superclass is NOT - * tablepanel, otherwise use the xtype itself. - */ - /** - * @cfg {Object} lockedViewConfig A view configuration to be applied to the - * locked side of the grid. Any conflicting configurations between lockedViewConfig - * and viewConfig will be overwritten by the lockedViewConfig. - */ - /** - * @cfg {Object} normalViewConfig A view configuration to be applied to the - * normal/unlocked side of the grid. Any conflicting configurations between normalViewConfig - * and viewConfig will be overwritten by the normalViewConfig. - */ - headerCounter: 0, - /** - * @cfg {Number} scrollDelta - * Number of pixels to scroll when scrolling the locked section with mousewheel. - */ - scrollDelta: 40, - /** - * @cfg {Object} lockedGridConfig - * Any special configuration options for the locked part of the grid - */ - /** - * @cfg {Object} normalGridConfig - * Any special configuration options for the normal part of the grid - */ - /** - * @cfg {Boolean} [split=false] - * Configure as true to place a resizing {@link Ext.resizer.Splitter splitter} between the locked - * and unlocked columns. - */ - /** - * @cfg {Object} [layout] - * By default, a lockable grid uses an {@link Ext.layout.container.HBox HBox} layout to arrange - * the two grids (possibly separated by a splitter). - * - * Using this config it is possible to specify a different layout to arrange the two grids. - */ - /** - * @cfg stateEvents - * @inheritdoc Ext.state.Stateful#cfg-stateEvents - * @localdoc Adds the following stateEvents: - * - * - {@link #event-lockcolumn} - * - {@link #event-unlockcolumn} - */ - lockedGridCls: Ext.baseCSSPrefix + 'grid-inner-locked', - normalGridCls: Ext.baseCSSPrefix + 'grid-inner-normal', - lockingBodyCls: Ext.baseCSSPrefix + 'grid-locking-body', - // i8n text - // - unlockText: 'Unlock', - // - // - lockText: 'Lock', - // - // Required for the Lockable Mixin. These are the configurations which will be copied to the - // normal and locked sub tablepanels - bothCfgCopy: [ - 'hideHeaders', - 'enableColumnHide', - 'enableColumnMove', - 'enableColumnResize', - 'sortableColumns', - 'multiColumnSort', - 'columnLines', - 'rowLines', - 'variableRowHeight', - 'numFromEdge', - 'trailingBufferZone', - 'leadingBufferZone', - 'scrollToLoadBuffer', - 'syncRowHeight' - ], - normalCfgCopy: [ - 'scroll' - ], - lockedCfgCopy: [], - /** - * @event processcolumns - * Fires when the configured (or **reconfigured**) column set is split into two depending on the {@link Ext.grid.column.Column#locked locked} flag. - * @param {Ext.grid.column.Column[]} lockedColumns The locked columns. - * @param {Ext.grid.column.Column[]} normalColumns The normal columns. - */ - /** - * @event lockcolumn - * Fires when a column is locked. - * @param {Ext.grid.Panel} this The gridpanel. - * @param {Ext.grid.column.Column} column The column being locked. - */ - /** - * @event unlockcolumn - * Fires when a column is unlocked. - * @param {Ext.grid.Panel} this The gridpanel. - * @param {Ext.grid.column.Column} column The column being unlocked. - */ - determineXTypeToCreate: function(lockedSide) { - var me = this, - typeToCreate, xtypes, xtypesLn, xtype, superxtype; - if (me.subGridXType) { - typeToCreate = me.subGridXType; - } else { - // Treeness only moves down into the locked side. - // The normal side is always just a grid - if (!lockedSide) { - return 'gridpanel'; - } - xtypes = this.getXTypes().split('/'); - xtypesLn = xtypes.length; - xtype = xtypes[xtypesLn - 1]; - superxtype = xtypes[xtypesLn - 2]; - if (superxtype !== 'tablepanel') { - typeToCreate = superxtype; - } else { - typeToCreate = xtype; - } - } - return typeToCreate; - }, - // injectLockable will be invoked before initComponent's parent class implementation - // is called, so throughout this method this. are configurations - injectLockable: function() { - // The child grids are focusable, not this one - this.focusable = false; - // ensure lockable is set to true in the TablePanel - this.lockable = true; - // Instruct the TablePanel it already has a view and not to create one. - // We are going to aggregate 2 copies of whatever TablePanel we are using - this.hasView = true; - var me = this, - scrollbarSize = Ext.getScrollbarSize(), - scrollbarWidth = scrollbarSize.width, - store = me.store = Ext.StoreManager.lookup(me.store), - lockedViewConfig = me.lockedViewConfig, - normalViewConfig = me.normalViewConfig, - Obj = Ext.Object, - // Hash of {lockedFeatures:[],normalFeatures:[]} - allFeatures, // Hash of {topPlugins:[],lockedPlugins:[],normalPlugins:[]} - allPlugins, lockedGrid, normalGrid, i, columns, lockedHeaderCt, normalHeaderCt, - viewConfig = me.viewConfig, - // When setting the loadMask value, the viewConfig wins if it is defined. - loadMaskCfg = viewConfig && viewConfig.loadMask, - loadMask = (loadMaskCfg !== undefined) ? loadMaskCfg : me.loadMask, - bufferedRenderer = me.bufferedRenderer, - clipVertLockedScrollbar = scrollbarWidth > 0 && Ext.supports.touchScroll !== 2, - rtl = me.getInherited().rtl; - allFeatures = me.constructLockableFeatures(); - // This is just a "shell" Panel which acts as a Container for the two grids and must not use the features - me.features = null; - // Distribute plugins to whichever Component needs them - allPlugins = me.constructLockablePlugins(); - me.plugins = allPlugins.topPlugins; - lockedGrid = { - id: me.id + '-locked', - $initParent: me, - isLocked: true, - bufferedRenderer: bufferedRenderer, - ownerGrid: me, - ownerLockable: me, - xtype: me.determineXTypeToCreate(true), - store: store, - // if the browser's scrollbars take up space we always reserve space for the - // vertical scrollbar on the locked side. This allows us to hide the vertical - // scrollbar by clipping it using the locked grid's body element. - reserveScrollbar: clipVertLockedScrollbar, - scrollable: { - indicators: { - x: true, - y: false - } - }, - scrollerOwner: false, - // Lockable does NOT support animations for Tree - // Because the right side is just a grid, and the grid view doen't animate bulk insertions/removals - animate: false, - border: false, - cls: me.lockedGridCls, - // Usually a layout in one side necessitates the laying out of the other side even if each is fully - // managed in both dimensions, and is therefore a layout root. - // The only situation that we do *not* want layouts to escape into the owning lockable assembly - // is when using a border layout and any of the border regions is floated from a collapsed state. - isLayoutRoot: function() { - return this.floatedFromCollapse || me.normalGrid.floatedFromCollapse; - }, - features: allFeatures.lockedFeatures, - plugins: allPlugins.lockedPlugins - }; - normalGrid = { - id: me.id + '-normal', - $initParent: me, - isLocked: false, - bufferedRenderer: bufferedRenderer, - ownerGrid: me, - ownerLockable: me, - xtype: me.determineXTypeToCreate(), - store: store, - // Pass down our reserveScrollbar to the normal side: - reserveScrollbar: me.reserveScrollbar, - scrollerOwner: false, - border: false, - cls: me.normalGridCls, - // As described above, isolate layouts when floated out from a collapsed border region. - isLayoutRoot: function() { - return this.floatedFromCollapse || me.lockedGrid.floatedFromCollapse; - }, - features: allFeatures.normalFeatures, - plugins: allPlugins.normalPlugins - }; - me.addCls(Ext.baseCSSPrefix + 'grid-locked'); - // Copy appropriate configurations to the respective aggregated tablepanel instances. - // Pass 4th param true to NOT exclude those settings on our prototype. - // Delete them from the master tablepanel. - Ext.copy(normalGrid, me, me.bothCfgCopy, true); - Ext.copy(lockedGrid, me, me.bothCfgCopy, true); - Ext.copy(normalGrid, me, me.normalCfgCopy, true); - Ext.copy(lockedGrid, me, me.lockedCfgCopy, true); - Ext.apply(normalGrid, me.normalGridConfig); - Ext.apply(lockedGrid, me.lockedGridConfig); - for (i = 0; i < me.normalCfgCopy.length; i++) { - delete me[me.normalCfgCopy[i]]; - } - for (i = 0; i < me.lockedCfgCopy.length; i++) { - delete me[me.lockedCfgCopy[i]]; - } - me.addStateEvents([ - 'lockcolumn', - 'unlockcolumn' - ]); - columns = me.processColumns(me.columns || [], lockedGrid); - lockedGrid.columns = columns.locked; - // If no locked columns, hide the locked grid - if (!lockedGrid.columns.items.length) { - lockedGrid.hidden = true; - } - normalGrid.columns = columns.normal; - if (!normalGrid.columns.items.length) { - normalGrid.hidden = true; - } - // normal grid should flex the rest of the width - normalGrid.flex = 1; - // Chain view configs to avoid mutating user's config - lockedGrid.viewConfig = lockedViewConfig = (lockedViewConfig ? Obj.chain(lockedViewConfig) : {}); - normalGrid.viewConfig = normalViewConfig = (normalViewConfig ? Obj.chain(normalViewConfig) : {}); - lockedViewConfig.loadingUseMsg = false; - lockedViewConfig.loadMask = false; - if (clipVertLockedScrollbar) { - if (rtl) { - lockedViewConfig.margin = '0 0 0 -' + scrollbarWidth + 'px'; - } else { - lockedViewConfig.margin = '0 -' + scrollbarWidth + 'px 0 0'; - } - } - normalViewConfig.loadMask = false; - if (viewConfig && viewConfig.id) { - Ext.log.warn('id specified on Lockable viewConfig, it will be shared between both views: "' + viewConfig.id + '"'); - } - Ext.applyIf(lockedViewConfig, viewConfig); - Ext.applyIf(normalViewConfig, viewConfig); - // Allow developer to configure the layout. - // Instantiate the layout so its type can be ascertained. - if (!me.initialConfig.layout) { - me.layout = { - type: 'hbox', - align: 'stretch' - }; - } - me.getLayout(); - // Sanity check the split config. - // Only allowed to insert a splitter between the two grids if it's a box layout - if (me.layout.type === 'border') { - if (me.split) { - lockedGrid.split = true; - } - if (!lockedGrid.region) { - lockedGrid.region = 'west'; - } - if (!normalGrid.region) { - normalGrid.region = 'center'; - } - me.addCls(Ext.baseCSSPrefix + 'grid-locked-split'); - } - if (!(me.layout instanceof Ext.layout.container.Box)) { - me.split = false; - } - // The LockingView is a pseudo view which owns the two grids. - // It listens for store events and relays the calls into each view bracketed by a layout suspension. - me.view = new Ext.grid.locking.View({ - loadMask: loadMask, - locked: lockedGrid, - normal: normalGrid, - ownerGrid: me - }); - // after creating the locking view we now have Grid instances for both locked and - // unlocked sides - lockedGrid = me.lockedGrid; - normalGrid = me.normalGrid; - // make the locked/unlocked sides mirror each other's vertical scroll positions. - normalGrid.getView().getScrollable().addPartner(lockedGrid.getView().getScrollable(), 'y'); - // Extract the instantiated views from the locking View. - // The locking View injects lockingGrid and normalGrid into this lockable panel. - // This is because during constraction, it must be possible for descendant components - // to navigate up to the owning lockable panel and then down into either side. - // We need to keep horizontal scrollbar appearance in the locked side - // synched with that on the normal side. - if (scrollbarSize.height && Ext.supports.touchScroll !== 2) { - lockedGrid.on({ - afterlayout: me.afterLockedViewLayout, - scope: me - }); - // Ensure the overflow flags have been calculated from the various overflow configs - lockedGrid.getView().getOverflowStyle(); - } - lockedHeaderCt = lockedGrid.headerCt; - normalHeaderCt = normalGrid.headerCt; - if (clipVertLockedScrollbar && !rtl) { - // if we are clipping the locked vertical scrollbar, we do not want the - // headerCt to reserve room for one - lockedHeaderCt.reserveScrollbar = false; - } - // The top grid, and the LockingView both need to have a headerCt which is usable. - // It is part of their private API that framework code uses when dealing with a grid or grid view - me.headerCt = me.view.headerCt = new Ext.grid.locking.HeaderContainer(me); - lockedHeaderCt.lockedCt = true; - lockedHeaderCt.lockableInjected = true; - normalHeaderCt.lockableInjected = true; - lockedHeaderCt.on({ - add: me.delaySyncLockedWidth, - remove: me.delaySyncLockedWidth, - columnshow: me.delaySyncLockedWidth, - columnhide: me.delaySyncLockedWidth, - sortchange: me.onLockedHeaderSortChange, - columnresize: me.delaySyncLockedWidth, - scope: me - }); - normalHeaderCt.on({ - add: me.delaySyncLockedWidth, - remove: me.delaySyncLockedWidth, - columnshow: me.delaySyncLockedWidth, - columnhide: me.delaySyncLockedWidth, - sortchange: me.onNormalHeaderSortChange, - scope: me - }); - me.modifyHeaderCt(); - me.items = [ - lockedGrid - ]; - if (me.split) { - me.addCls(Ext.baseCSSPrefix + 'grid-locked-split'); - me.items[1] = { - xtype: 'splitter' - }; - } - me.items.push(normalGrid); - me.relayHeaderCtEvents(lockedHeaderCt); - me.relayHeaderCtEvents(normalHeaderCt); - // The top level Lockable container does not get bound to the store, so we need to programatically add the relayer so that - // The filterchange state event is fired. - // - // TreePanel also relays the beforeload and load events, so - me.storeRelayers = me.relayEvents(store, [ - /** - * @event filterchange - * @inheritdoc Ext.data.Store#filterchange - */ - 'filterchange', - /** - * @event groupchange - * @inheritdoc Ext.data.Store#groupchange - */ - 'groupchange', - /** - * @event beforeload - * @inheritdoc Ext.data.Store#beforeload - */ - 'beforeload', - /** - * @event load - * @inheritdoc Ext.data.Store#load - */ - 'load' - ]); - // Only need to relay from the normalGrid. Since it's created after the lockedGrid, - // we can be confident to only listen to it. - me.gridRelayers = me.relayEvents(normalGrid, [ - /** - * @event viewready - * @inheritdoc Ext.panel.Table#viewready - */ - 'viewready' - ]); - }, - afterInjectLockable: function() { - delete this.lockedGrid.$initParent; - delete this.normalGrid.$initParent; - }, - getLockingViewConfig: function() { - return { - xclass: 'Ext.grid.locking.View', - locked: this.lockedGrid, - normal: this.normalGrid, - panel: this - }; - }, - processColumns: function(columns, lockedGrid) { - // split apart normal and locked - var me = this, - i, len, column, - cp = new Ext.grid.header.Container({ - "$initParent": me - }), - lockedHeaders = [], - normalHeaders = [], - lockedHeaderCt = { - itemId: 'lockedHeaderCt', - stretchMaxPartner: '^^>>#normalHeaderCt', - items: lockedHeaders - }, - normalHeaderCt = { - itemId: 'normalHeaderCt', - stretchMaxPartner: '^^>>#lockedHeaderCt', - items: normalHeaders - }, - result = { - locked: lockedHeaderCt, - normal: normalHeaderCt - }, - copy; - // In case they specified a config object with items... - if (Ext.isObject(columns)) { - Ext.applyIf(lockedHeaderCt, columns); - Ext.applyIf(normalHeaderCt, columns); - copy = Ext.apply({}, columns); - delete copy.items; - Ext.apply(cp, copy); - columns = columns.items; - } - // Treat the column header as though we're just creating an instance, since this - // doesn't follow the normal column creation pattern - cp.constructing = true; - for (i = 0 , len = columns.length; i < len; ++i) { - column = columns[i]; - // Use the HeaderContainer object to correctly configure and create the column. - // MUST instantiate now because the locked or autoLock config which we read here might be in the prototype. - // MUST use a Container instance so that defaults from an object columns config get applied. - if (!column.isComponent) { - column = cp.applyDefaults(column); - column.$initParent = cp; - column = cp.lookupComponent(column); - delete column.$initParent; - } - // mark the column as processed so that the locked attribute does not - // trigger the locked subgrid to try to become a split lockable grid itself. - column.processed = true; - if (column.locked || column.autoLock) { - lockedHeaders.push(column); - } else { - normalHeaders.push(column); - } - } - me.fireEvent('processcolumns', me, lockedHeaders, normalHeaders); - cp.destroy(); - return result; - }, - // Due to automatic component border setting using inline style, to create the scrollbar-replacing - // bottom border, we have to postprocess the locked view *after* render. - // A tall bottom border takes the place of a horiz scrollbar if the opposite side has a horiz scrollbar. - // When we can use overflow-x: scroll to create a matching scrollbar, we do this instead. - afterLockedViewLayout: function() { - var me = this, - lockedGrid = me.lockedGrid, - normalGrid = me.normalGrid, - lockedView = lockedGrid.getView(), - normalView = normalGrid.getView(), - lockedViewHorizScrollBar = lockedView.scrollFlags.x && lockedGrid.headerCt.tooNarrow, - normalViewHorizScrollBar = normalView.scrollFlags.x && normalGrid.headerCt.tooNarrow, - normalScroller = normalView.getScrollable(), - lockedScroller = lockedView.getScrollable(); - if (lockedViewHorizScrollBar !== normalViewHorizScrollBar) { - if (lockedViewHorizScrollBar) { - normalScroller.setX('scroll'); - lockedScroller.setX(true); - } else { - lockedScroller.setX('scroll'); - normalScroller.setX(true); - } - } else { - lockedScroller.setX(normalViewHorizScrollBar ? 'scroll' : true); - normalScroller.setX(true); - } - }, - ensureLockedVisible: function() { - this.lockedGrid.ensureVisible.apply(this.lockedGrid, arguments); - this.normalGrid.ensureVisible.apply(this.normalGrid, arguments); - }, - onLockedViewMouseWheel: function(e) { - var me = this, - deltaY = -me.scrollDelta * e.getWheelDeltas().y, - lockedView = me.lockedGrid.getView(), - lockedViewElDom = lockedView.el.dom, - scrollTop, verticalCanScrollDown, verticalCanScrollUp; - if (!me.ignoreMousewheel) { - if (lockedViewElDom) { - scrollTop = lockedView.getScrollY(); - verticalCanScrollDown = scrollTop !== lockedViewElDom.scrollHeight - lockedViewElDom.clientHeight; - verticalCanScrollUp = scrollTop !== 0; - } - if ((deltaY < 0 && verticalCanScrollUp) || (deltaY > 0 && verticalCanScrollDown)) { - e.stopEvent(); - // Inhibit processing of any scroll events we *may* cause here. - // Some OSs do not fire a scroll event when we set the scrollTop of an overflow:hidden element, - // so we invoke the scroll handler programatically below. - scrollTop += deltaY; - lockedView.setScrollY(scrollTop); - me.normalGrid.getView().setScrollY(scrollTop); - // Invoke the scroll event handler programatically to sync everything. - me.onNormalViewScroll(); - } - } - }, - onLockedViewScroll: function() { - var me = this, - lockedView = me.lockedGrid.getView(), - normalView = me.normalGrid.getView(), - lockedScrollTop = lockedView.getScrollY(), - normalScrollTop = normalView.getScrollY(), - normalTable, lockedTable; - if (normalScrollTop !== lockedScrollTop) { - normalView.setScrollY(lockedScrollTop); - // For buffered views, the absolute position is important as well as scrollTop - if (normalView.bufferedRenderer) { - lockedTable = lockedView.body.dom; - normalTable = normalView.body.dom; - normalTable.style.position = 'absolute'; - normalTable.style.top = lockedTable.style.top; - } - } - }, - onNormalViewScroll: function() { - var me = this, - lockedView = me.lockedGrid.getView(), - normalView = me.normalGrid.getView(), - lockedScrollTop = lockedView.getScrollY(), - normalScrollTop = normalView.getScrollY(), - lockedRowContainer; - if (normalScrollTop !== lockedScrollTop) { - lockedView.setScrollY(normalScrollTop); - // For buffered views, the absolute position is important as well as scrollTop - if (normalView.bufferedRenderer) { - lockedRowContainer = lockedView.body; - // If we have attached the Fly to a DOM (will not have happened if all locked columns are hidden) - if (lockedRowContainer.dom) { - lockedRowContainer.dom.style.position = 'absolute'; - lockedRowContainer.translate(null, normalView.bufferedRenderer.bodyTop); - } - } - } - }, - /** - * Synchronizes the row heights between the locked and non locked portion of the grid for each - * row. If one row is smaller than the other, the height will be increased to match the larger one. - */ - syncRowHeights: function() { - // This is now called on animationFrame. It may have been destroyed in the interval. - if (!this.destroyed) { - var me = this, - normalView = me.normalGrid.getView(), - lockedView = me.lockedGrid.getView(), - // These will reset any forced height styles from the last sync - normalSync = normalView.syncRowHeightBegin(), - lockedSync = lockedView.syncRowHeightBegin(), - scrollTop; - // Now bulk measure everything - normalView.syncRowHeightMeasure(normalSync); - lockedView.syncRowHeightMeasure(lockedSync); - // Now write out all the explicit heights we need to sync up - normalView.syncRowHeightFinish(normalSync, lockedSync); - lockedView.syncRowHeightFinish(lockedSync, normalSync); - // Synchronize the scrollTop positions of the two views - scrollTop = normalView.getScrollY(); - lockedView.setScrollY(scrollTop); - } - }, - // inject Lock and Unlock text - // Hide/show Lock/Unlock options - modifyHeaderCt: function() { - var me = this; - me.lockedGrid.headerCt.getMenuItems = me.getMenuItems(me.lockedGrid.headerCt.getMenuItems, true); - me.normalGrid.headerCt.getMenuItems = me.getMenuItems(me.normalGrid.headerCt.getMenuItems, false); - me.lockedGrid.headerCt.showMenuBy = Ext.Function.createInterceptor(me.lockedGrid.headerCt.showMenuBy, me.showMenuBy); - me.normalGrid.headerCt.showMenuBy = Ext.Function.createInterceptor(me.normalGrid.headerCt.showMenuBy, me.showMenuBy); - }, - onUnlockMenuClick: function() { - this.unlock(); - }, - onLockMenuClick: function() { - this.lock(); - }, - showMenuBy: function(clickEvent, t, header) { - var menu = this.getMenu(), - unlockItem = menu.down('#unlockItem'), - lockItem = menu.down('#lockItem'), - sep = unlockItem.prev(); - if (header.lockable === false) { - sep.hide(); - unlockItem.hide(); - lockItem.hide(); - } else { - sep.show(); - unlockItem.show(); - lockItem.show(); - if (!unlockItem.initialConfig.disabled) { - unlockItem.setDisabled(header.lockable === false); - } - if (!lockItem.initialConfig.disabled) { - lockItem.setDisabled(!header.isLockable()); - } - } - }, - getMenuItems: function(getMenuItems, locked) { - var me = this, - unlockText = me.unlockText, - lockText = me.lockText, - unlockCls = Ext.baseCSSPrefix + 'hmenu-unlock', - lockCls = Ext.baseCSSPrefix + 'hmenu-lock', - unlockHandler = me.onUnlockMenuClick.bind(me), - lockHandler = me.onLockMenuClick.bind(me); - // runs in the scope of headerCt - return function() { - // We cannot use the method from HeaderContainer's prototype here - // because other plugins or features may already have injected an implementation - var o = getMenuItems.call(this); - o.push('-', { - itemId: 'unlockItem', - iconCls: unlockCls, - text: unlockText, - handler: unlockHandler, - disabled: !locked - }); - o.push({ - itemId: 'lockItem', - iconCls: lockCls, - text: lockText, - handler: lockHandler, - disabled: locked - }); - return o; - }; - }, - syncTaskDelay: 1, - delaySyncLockedWidth: function() { - var me = this, - task = me.syncLockedWidthTask; - // If no rows to sync, do not schedule a sync operation - if (!me.view.all.getCount()) { - return; - } - if (!task) { - task = me.syncLockedWidthTask = new Ext.util.DelayedTask(me.syncLockedWidth, me); - } - // Only useful for unit testing so we don't have to mess around with timers - if (me.syncTaskDelay === 0) { - me.syncLockedWidth(); - } else { - task.delay(1); - } - }, - /** - * @private - * Updates the overall view after columns have been resized, or moved from - * the locked to unlocked side or vice-versa. - * - * If all columns are removed from either side, that side must be hidden, and the - * sole remaining column owning grid then becomes *the* grid. It must flex to occupy the - * whole of the locking view. And it must also allow scrolling. - * - * If columns are shared between the two sides, the *locked* grid shrinkwraps the - * width of the visible locked columns while the normal grid flexes in what space remains. - * - * @return {Boolean} `true` if there are visible locked columns which need refreshing. - * - */ - syncLockedWidth: function() { - var me = this, - rendered = me.rendered, - locked = me.lockedGrid, - lockedView = locked.view, - normal = me.normalGrid, - lockedColCount = locked.getVisibleColumnManager().getColumns().length, - normalColCount = normal.getVisibleColumnManager().getColumns().length, - task = me.syncLockedWidthTask; - // If we are called directly, veto any existing task. - if (task) { - task.cancel(); - } - Ext.suspendLayouts(); - // If there are still visible normal columns, then the normal grid will flex - // while we effectively shrinkwrap the width of the locked columns - if (normalColCount) { - normal.show(); - if (lockedColCount) { - // The locked grid shrinkwraps the total column width while the normal grid flexes in what remains - // UNLESS it has been set to forceFit - if (rendered && locked.shrinkWrapColumns && !locked.headerCt.forceFit) { - delete locked.flex; - // Don't pass the purge flag here - // Use gridPanelBorderWidth as measured in Ext.table.Panel#onRender - // TODO: Use shrinkWrapDock on the locked grid when it works. - locked.setWidth(locked.headerCt.getTableWidth() + locked.gridPanelBorderWidth); - } - locked.addCls(me.lockedGridCls); - locked.show(); - if (locked.split) { - me.child('splitter').show(); - me.addCls(Ext.baseCSSPrefix + 'grid-locked-split'); - } - } else { - // No visible locked columns: hide the locked grid - // We also need to trigger a clearViewEl to clear out any - // old dom nodes - if (rendered) { - locked.getView().clearViewEl(true); - } - locked.hide(); - if (locked.split) { - me.child('splitter').hide(); - me.removeCls(Ext.baseCSSPrefix + 'grid-locked-split'); - } - } - // Only if there is going to be an upcoming layout to correct the horizontal scrollbar setting. - if (Ext.supports.touchScroll !== 2 && Ext.Component.pendingLayouts) { - // We may have previously set horizontal placeholder scrollbar on the locked - // view to match the unlocked side. Undo this before continuing, so that - // the horizontal scrollbar does not affect the layout of the columns by - // possibly triggering a vertical scrollbar as well - lockedView.getScrollable().setX(true); - } - // Ignore mousewheel events if the view is configured to scroll vertically - if (rendered) { - me.ignoreMousewheel = lockedView.scrollFlags.y; - } - } else // There are no normal grid columns. The "locked" grid has to be *the* - // grid, and cannot have a shrinkwrapped width, but must flex the entire width. - { - normal.hide(); - // The locked now becomes *the* grid and has to flex to occupy the full view width - locked.flex = 1; - delete locked.width; - locked.removeCls(me.lockedGridCls); - locked.show(); - me.ignoreMousewheel = true; - } - Ext.resumeLayouts(true); - return [ - lockedColCount, - normalColCount - ]; - }, - onLockedHeaderSortChange: Ext.emptyFn, - onNormalHeaderSortChange: Ext.emptyFn, - // going from unlocked section to locked - /** - * Locks the activeHeader as determined by which menu is open OR a header - * as specified. - * @param {Ext.grid.column.Column} [header] Header to unlock from the locked section. - * Defaults to the header which has the menu open currently. - * @param {Number} [toIdx] The index to move the unlocked header to. - * Defaults to appending as the last item. - * @private - */ - lock: function(activeHd, toIdx, toCt) { - var me = this, - normalGrid = me.normalGrid, - lockedGrid = me.lockedGrid, - normalView = normalGrid.view, - lockedView = lockedGrid.view, - normalHCt = normalGrid.headerCt, - refreshFlags, ownerCt, hadFocus, normalScrollY; - activeHd = activeHd || normalHCt.getMenu().activeHeader; - hadFocus = activeHd.hasFocus; - toCt = toCt || lockedGrid.headerCt; - ownerCt = activeHd.ownerCt; - // isLockable will test for making the locked side too wide - if (!activeHd.isLockable()) { - return; - } - // if column was previously flexed, get/set current width - // and remove the flex - if (activeHd.flex && lockedGrid.shrinkWrapColumns) { - activeHd.width = activeHd.getWidth(); - activeHd.flex = null; - } - Ext.suspendLayouts(); - // If hidden, we need to show it now or the locked headerCt's VisibleColumnManager may be out of sync as - // headers are only added to a visible manager if they are not explicity hidden or hierarchically hidden. - if (lockedGrid.hidden) { - // The locked side's BufferedRenderer has never has a resize passed in, so its viewSize will be the default - // viewSize, out of sync with the normal side. Synchronize the viewSize before the two sides are refreshed. - if (!lockedGrid.componentLayoutCounter) { - if (lockedView.bufferedRenderer) { - lockedView.bufferedRenderer.onViewResize(lockedView, 0, normalView.getHeight()); - } - normalScrollY = normalView.getScrollY(); - } - lockedGrid.show(); - } - // We decide which views to refresh. Do not let the grids do it in response to column changes - normalView.blockRefresh = lockedView.blockRefresh = true; - // Keep the column in the hierarchy during the move. - // So that grid.isAncestor(column) still returns true, and SpreadsheetModel does not deselect - activeHd.ownerCmp = activeHd.ownerCt; - ownerCt.remove(activeHd, false); - activeHd.locked = true; - // Flag to the locked column add listener to do nothing - if (Ext.isDefined(toIdx)) { - toCt.insert(toIdx, activeHd); - } else { - toCt.add(activeHd); - } - normalView.blockRefresh = lockedView.blockRefresh = false; - activeHd.ownerCmp = null; - refreshFlags = me.syncLockedWidth(); - if (refreshFlags[0]) { - lockedGrid.getView().refreshView(); - } - if (refreshFlags[1]) { - normalGrid.getView().refreshView(); - } - me.fireEvent('lockcolumn', me, activeHd); - Ext.resumeLayouts(true); - if (normalScrollY) { - lockedView.setScrollY(normalScrollY); - normalView.setScrollY(normalScrollY); - } - if (hadFocus) { - activeHd.focus(); - } - }, - // going from locked section to unlocked - /** - * Unlocks the activeHeader as determined by which menu is open OR a header - * as specified. - * @param {Ext.grid.column.Column} [header] Header to unlock from the locked section. - * Defaults to the header which has the menu open currently. - * @param {Number} [toIdx=0] The index to move the unlocked header to. - * @private - */ - unlock: function(activeHd, toIdx, toCt) { - var me = this, - normalGrid = me.normalGrid, - lockedGrid = me.lockedGrid, - normalView = normalGrid.view, - lockedView = lockedGrid.view, - lockedHCt = lockedGrid.headerCt, - refreshFlags, hadFocus; - // Unlocking; user expectation is that the unlocked column is inserted at the beginning. - if (!Ext.isDefined(toIdx)) { - toIdx = 0; - } - activeHd = activeHd || lockedHCt.getMenu().activeHeader; - hadFocus = activeHd.hasFocus; - toCt = toCt || normalGrid.headerCt; - Ext.suspendLayouts(); - // We decide which views to refresh. Do not let the grids do it in response to column changes - normalView.blockRefresh = lockedView.blockRefresh = true; - // Keep the column in the hierarchy during the move. - // So that grid.isAncestor(column) still returns true, and SpreadsheetModel does not deselect - activeHd.ownerCmp = activeHd.ownerCt; - activeHd.ownerCt.remove(activeHd, false); - activeHd.locked = false; - toCt.insert(toIdx, activeHd); - normalView.blockRefresh = lockedView.blockRefresh = false; - activeHd.ownerCmp = null; - // syncLockedWidth returns visible column counts for both grids. - // only refresh what needs refreshing - refreshFlags = me.syncLockedWidth(); - if (refreshFlags[0]) { - lockedGrid.getView().refreshView(); - } - if (refreshFlags[1]) { - normalGrid.getView().refreshView(); - } - me.fireEvent('unlockcolumn', me, activeHd); - Ext.resumeLayouts(true); - if (hadFocus) { - activeHd.focus(); - } - }, - // we want to totally override the reconfigure behaviour here, since we're creating 2 sub-grids - reconfigureLockable: function(store, columns) { - var me = this, - oldStore = me.store, - lockedGrid = me.lockedGrid, - normalGrid = me.normalGrid, - view, loadMask; - // Note that we need to process the store first in case one or more passed columns (if there are any) - // have active gridfilters with values which would filter the currently-bound store. - if (store && store !== oldStore) { - store = Ext.data.StoreManager.lookup(store); - me.store = store; - lockedGrid.view.blockRefresh = normalGrid.view.blockRefresh = true; - lockedGrid.bindStore(store); - // Subsidiary views have their bindStore changed because they must not - // bind listeners themselves. This view listens and relays calls to each view. - // BUT the dataSource and store properties must be set - view = lockedGrid.view; - view.store = store; - // If the dataSource being used by the View is *not* a FeatureStore - // (a modified view of the base Store injected by a Feature) - // Then we promote the store to be the dataSource. - // If it was a FeatureStore, then it must not be changed. A FeatureStore is mutated - // by the Feature to respond to changes in the underlying Store. - if (!view.dataSource.isFeatureStore) { - view.dataSource = store; - } - if (view.bufferedRenderer) { - view.bufferedRenderer.bindStore(store); - } - normalGrid.bindStore(store); - view = normalGrid.view; - view.store = store; - // If the dataSource being used by the View is *not* a FeatureStore - // (a modified view of the base Store injected by a Feature) - // Then we promote the store to be the dataSource. - // If it was a FeatureStore, then it must not be changed. A FeatureStore is mutated - // by the Feature to respond to changes in the underlying Store. - if (!view.dataSource.isFeatureStore) { - view.dataSource = store; - } - if (view.bufferedRenderer) { - view.bufferedRenderer.bindStore(store); - } - me.view.store = store; - // binding mask to new store - loadMask = me.view.loadMask; - if (loadMask && loadMask.isLoadMask) { - loadMask.bindStore(store); - } - me.view.bindStore(normalGrid.view.dataSource, false, 'dataSource'); - lockedGrid.view.blockRefresh = normalGrid.view.blockRefresh = false; - } - if (columns) { - // Both grids must not react to the headers being changed (See panel/Table#onHeadersChanged) - lockedGrid.reconfiguring = normalGrid.reconfiguring = true; - lockedGrid.headerCt.removeAll(); - normalGrid.headerCt.removeAll(); - columns = me.processColumns(columns, lockedGrid); - // Flag to the locked column add listener to do nothing - lockedGrid.headerCt.add(columns.locked.items); - normalGrid.headerCt.add(columns.normal.items); - lockedGrid.reconfiguring = normalGrid.reconfiguring = false; - // Ensure locked grid is set up correctly with correct width and bottom border, - // and that both grids' visibility and scrollability status is correct - me.syncLockedWidth(); - } - me.refreshCounter = lockedGrid.view.refreshCounter; - }, - afterReconfigureLockable: function() { - var lockedView = this.lockedGrid.getView(); - // If the counter hasn't changed since where we saved it previously, we haven't refreshed, - // so kick it off now. - if (this.refreshCounter === lockedView.refreshCounter) { - this.view.refresh(); - } - }, - constructLockableFeatures: function() { - var features = this.features, - feature, featureClone, lockedFeatures, normalFeatures, - i = 0, - len; - if (features) { - if (!Ext.isArray(features)) { - features = [ - features - ]; - } - lockedFeatures = []; - normalFeatures = []; - len = features.length; - for (; i < len; i++) { - feature = features[i]; - if (!feature.isFeature) { - feature = Ext.create('feature.' + feature.ftype, feature); - } - switch (feature.lockableScope) { - case 'locked': - lockedFeatures.push(feature); - break; - case 'normal': - normalFeatures.push(feature); - break; - default: - feature.lockableScope = 'both'; - lockedFeatures.push(feature); - normalFeatures.push(featureClone = feature.clone()); - // When cloned to either side, each gets a "lockingPartner" reference to the other - featureClone.lockingPartner = feature; - feature.lockingPartner = featureClone; - } - } - } - return { - normalFeatures: normalFeatures, - lockedFeatures: lockedFeatures - }; - }, - constructLockablePlugins: function() { - var plugins = this.plugins, - plugin, normalPlugin, lockedPlugin, topPlugins, lockedPlugins, normalPlugins, - i = 0, - len, lockableScope, pluginCls; - if (plugins) { - if (!Ext.isArray(plugins)) { - plugins = [ - plugins - ]; - } - topPlugins = []; - lockedPlugins = []; - normalPlugins = []; - len = plugins.length; - for (; i < len; i++) { - plugin = plugins[i]; - // Plugin will most likely already have been instantiated by the Component constructor - if (plugin.init) { - lockableScope = plugin.lockableScope; - } else // If not, it's because of late addition through a subclass's initComponent implementation, so we - // must ascertain the lockableScope directly from the class. - { - pluginCls = plugin.ptype ? Ext.ClassManager.getByAlias(('plugin.' + plugin.ptype)) : Ext.ClassManager.get(plugin.xclass); - lockableScope = pluginCls.prototype.lockableScope; - } - switch (lockableScope) { - case 'both': - lockedPlugins.push(lockedPlugin = plugin.clonePlugin()); - normalPlugins.push(normalPlugin = plugin.clonePlugin()); - // When cloned to both sides, each gets a "lockingPartner" reference to the other - lockedPlugin.lockingPartner = normalPlugin; - normalPlugin.lockingPartner = lockedPlugin; - // If the plugin has to be propagated down to both, a new plugin config object must be given to that side - // and this plugin must be destroyed. - Ext.destroy(plugin); - break; - case 'locked': - lockedPlugins.push(plugin); - break; - case 'normal': - normalPlugins.push(plugin); - break; - default: - topPlugins.push(plugin); - } - } - } - return { - topPlugins: topPlugins, - normalPlugins: normalPlugins, - lockedPlugins: lockedPlugins - }; - }, - destroyLockable: function() { - // The locking view isn't a "real" view, so we need to destroy it manually - var me = this, - task = me.syncLockedWidthTask; - if (task) { - task.cancel(); - me.syncLockedWidthTask = null; - } - // Release interceptors created in modifyHeaderCt - if (me.lockedGrid && me.lockedGrid.headerCt) { - me.lockedGrid.headerCt.showMenuBy = null; - } - if (me.normalGrid && me.normalGrid.headerCt) { - me.normalGrid.headerCt.showMenuBy = null; - } - Ext.destroy(me.view, me.headerCt); - } -}, function() { - this.borrow(Ext.Component, [ - 'constructPlugin' - ]); -}); - -/** - * @private - * Implements buffered rendering of a grid, allowing users to scroll - * through thousands of records without the performance penalties of - * rendering all the records into the DOM at once. - * - * The number of rows rendered outside the visible area can be controlled by configuring the plugin. - * - * Users should not instantiate this class. It is instantiated automatically - * and applied to all grids. - * - * ## Implementation notes - * - * This class monitors scrolling of the {@link Ext.view.Table - * TableView} within a {@link Ext.grid.Panel GridPanel} to render a small section of - * the dataset. - * - */ -Ext.define('Ext.grid.plugin.BufferedRenderer', { - extend: 'Ext.AbstractPlugin', - alias: 'plugin.bufferedrenderer', - /** - * @property {Boolean} isBufferedRenderer - * `true` in this class to identify an object as an instantiated BufferedRenderer, or subclass thereof. - */ - isBufferedRenderer: true, - lockableScope: 'both', - /** - * @cfg {Number} - * The zone which causes new rows to be appended to the view. As soon as the edge - * of the rendered grid is this number of rows from the edge of the viewport, the view is moved. - */ - numFromEdge: 2, - /** - * @cfg {Number} - * The number of extra rows to render on the trailing side of scrolling - * **outside the {@link #numFromEdge}** buffer as scrolling proceeds. - */ - trailingBufferZone: 10, - /** - * @cfg {Number} - * The number of extra rows to render on the leading side of scrolling - * **outside the {@link #numFromEdge}** buffer as scrolling proceeds. - */ - leadingBufferZone: 20, - /** - * @cfg {Boolean} [synchronousRender=true] - * By default, on detection of a scroll event which brings the end of the rendered table within - * `{@link #numFromEdge}` rows of the grid viewport, if the required rows are available in the Store, - * the BufferedRenderer will render rows from the Store *immediately* before returning from the event handler. - * This setting helps avoid the impression of whitespace appearing during scrolling. - * - * Set this to `false` to defer the render until the scroll event handler exits. This allows for faster - * scrolling, but also allows whitespace to be more easily scrolled into view. - * - */ - synchronousRender: true, - /** - * @cfg {Number} - * This is the time in milliseconds to buffer load requests when the store is a {@link Ext.data.BufferedStore buffered store} - * and a page required for rendering is not present in the store's cache and needs loading. - */ - scrollToLoadBuffer: 200, - /** - * @private - */ - viewSize: 100, - /** - * @private - */ - rowHeight: 21, - /** - * @property {Number} position - * Current pixel scroll position of the associated {@link Ext.view.Table View}. - */ - position: 0, - lastScrollDirection: 1, - bodyTop: 0, - scrollHeight: 0, - loadId: 0, - // Initialize this as a plugin - init: function(grid) { - var me = this, - view = grid.view, - viewListeners = { - scroll: me.onViewScroll, - scrollend: me.onViewScrollEnd, - refresh: me.onViewRefresh, - columnschanged: me.checkVariableRowHeight, - boxready: me.onViewBoxReady, - scope: me, - destroyable: true - }, - initialConfig = view.initialConfig; - // If we are going to be handling a NodeStore then it's driven by node addition and removal, *not* refreshing. - // The view overrides required above change the view's onAdd and onRemove behaviour to call onDataRefresh when necessary. - if (grid.isTree || (grid.ownerLockable && grid.ownerLockable.isTree)) { - view.blockRefresh = false; - // Set a load mask if undefined in the view config. - if (initialConfig && initialConfig.loadMask === undefined) { - view.loadMask = true; - } - } - if (view.positionBody) { - viewListeners.refresh = me.onViewRefresh; - } - // Only play the pointer-events;none trick on the platform it is needed on. - // Only needed when using DOM scrolling on WebKit. - // WebKit does a browser layout when you change the pointer-events style. - if (Ext.isWebKit && Ext.supports.touchScroll !== 2) { - me.needsPointerEventsFix = true; - viewListeners.scrollEnd = me.onViewScrollEnd; - } - me.grid = grid; - me.view = view; - me.isRTL = view.getInherited().rtl; - view.bufferedRenderer = me; - view.preserveScrollOnRefresh = true; - view.animate = false; - // It doesn't matter if it's a FeatureStore or a DataStore. The important thing is to only bind the same Type of - // store in future operations! - me.bindStore(view.dataSource); - // Use a configured rowHeight in the view - if (view.hasOwnProperty('rowHeight')) { - me.rowHeight = view.rowHeight; - } - me.position = 0; - me.viewListeners = view.on(viewListeners); - }, - // Keep the variableRowHeight property correct WRT variable row heights being possible. - checkVariableRowHeight: function() { - this.variableRowHeight = this.view.hasVariableRowHeight(); - }, - bindStore: function(newStore) { - var me = this, - currentStore = me.store, - view = me.view; - // If the grid was configured with a feature such as Grouping that binds a FeatureStore (GroupStore, in its case) as - // the view's dataSource, we must continue to use the same Type of store. - // - // Note that reconfiguring the grid can call into here. - if (currentStore && currentStore.isFeatureStore) { - return; - } - if (currentStore) { - me.unbindStore(); - } - me.storeListeners = newStore.on({ - scope: me, - groupchange: me.onStoreGroupChange, - clear: me.onStoreClear, - beforeload: me.onBeforeStoreLoad, - load: me.onStoreLoad, - destroyable: true - }); - me.store = newStore; - // If the view has acquired a size, calculate a new view size and scroll range when the store changes. - if (me.view.componentLayout.layoutCount) { - // Delete whatever our last viewSize might have been, and fall back to the prototype's default. - delete me.viewSize; - if (newStore.isBufferedStore) { - newStore.setViewSize(me.viewSize); - } - me.onViewResize(me.view, 0, me.view.getHeight()); - } - }, - unbindStore: function() { - this.storeListeners.destroy(); - this.storeListeners = this.store = null; - }, - // Disable handling of scroll events until the load is finished - onBeforeStoreLoad: function(store) { - var me = this, - view = me.view; - if (view && view.refreshCounter) { - // Unless we are loading tree nodes, or have preserveScrollOnReload, set scroll position and row range back to zero. - if (store.isTreeStore || view.preserveScrollOnReload) { - me.nextRefreshStartIndex = view.all.startIndex; - } else { - if (me.scrollTop !== 0) { - // Zero position tracker so that next scroll event will not trigger any action - me.setBodyTop(me.bodyTop = me.scrollTop = me.position = me.scrollHeight = me.nextRefreshStartIndex = 0); - view.setScrollY(0); - } - } - me.lastScrollDirection = me.scrollOffset = null; - } - me.disable(); - }, - // Re-enable scroll event handling on load. - onStoreLoad: function() { - this.enable(); - }, - onStoreClear: function() { - var me = this, - view = me.view; - // Do not do anything if view is not rendered, or if the reason for cache clearing is store destruction - if (view.rendered && !me.store.destroyed) { - if (me.scrollTop !== 0) { - // Zero position tracker so that next scroll event will not trigger any action - me.bodyTop = me.scrollTop = me.position = me.scrollHeight = 0; - me.nextRefreshStartIndex = null; - view.setScrollY(0); - } - // TableView does not add a Store Clear listener if there's a BufferedRenderer - // We handle that here. - view.refresh(); - me.lastScrollDirection = me.scrollOffset = null; - } - }, - // If the store is not grouped, we can switch to fixed row height mode - onStoreGroupChange: function(store) { - this.refreshSize(); - }, - onViewBoxReady: function(view) { - this.refreshScroller(view, this.scrollHeight); - }, - onViewRefresh: function(view, records) { - var me = this, - rows = view.all, - height; - // Recheck the variability of row height in the view. - me.checkVariableRowHeight(); - // The first refresh on the leading edge of the initial layout will mean that the - // View has not had the sizes of flexed columns calculated and flushed yet. - // So measurement of DOM height for calculation of an approximation of the variableRowHeight would be premature. - // And measurement of the body width would be premature because of uncalculated flexes. - if (!view.componentLayoutCounter && (view.headerCt.down('{flex}') || me.variableRowHeight)) { - view.on({ - boxready: Ext.Function.pass(me.onViewRefresh, [ - view, - records - ], me), - single: true - }); - // AbstractView will call refreshSize() immediately after firing the 'refresh' - // event; we need to skip that run for the reasons stated above. - me.skipNextRefreshSize = true; - return; - } - me.skipNextRefreshSize = false; - // If we are instigating the refresh, we will have already called refreshSize in doRefreshView - if (me.refreshing) { - return; - } - me.refreshSize(); - if (me.scrollTop !== view.getScrollY()) { - // The view may have refreshed and scrolled to the top, for example - // on a sort. If so, it's as if we scrolled to the top, so we'll simulate - // it here. - me.onViewScroll(); - me.onViewScrollEnd(); - } else { - if (!me.hasOwnProperty('bodyTop')) { - me.bodyTop = rows.startIndex * me.rowHeight; - view.setScrollY(me.bodyTop); - } - me.setBodyTop(me.bodyTop); - // With new data, the height may have changed, so recalculate the rowHeight and viewSize. - // This will either add or remove some rows. - height = view.getHeight(); - if (rows.getCount() && height > 0) { - me.onViewResize(view, null, height); - // If we repaired the view by adding or removing records, then keep the records array - // consistent with what is there for subsequent listeners. - // For example the WidgetColumn listener which post-processes all rows: https://sencha.jira.com/browse/EXTJS-13942 - if (records && (rows.getCount() !== records.length)) { - records.length = 0; - records.push.apply(records, me.store.getRange(rows.startIndex, rows.endIndex)); - } - } - } - }, - /** - * @private - * @param {Ext.layout.ContextItem} ownerContext The view's layout context - * Called before the start of a view's layout run - */ - beforeTableLayout: function(ownerContext) { - var dom = this.view.body.dom; - if (dom) { - ownerContext.bodyHeight = dom.offsetHeight; - ownerContext.bodyWidth = dom.offsetWidth; - } - }, - /** - * @private - * @param {Ext.layout.ContextItem} ownerContext The view's layout context - * Called when a view's layout run is complete. - */ - afterTableLayout: function(ownerContext) { - var me = this, - view = me.view, - renderedBlockHeight; - // The rendered block has changed height. - // This could happen if a cellWrap: true column has changed width. - // We need to recalculate row height and scroll range - if (ownerContext.bodyHeight && view.body.dom) { - delete me.rowHeight; - me.refreshSize(); - renderedBlockHeight = view.body.dom.offsetHeight; - if (renderedBlockHeight !== ownerContext.bodyHeight) { - me.onViewResize(view, null, view.el.lastBox.height); - // The layout has caused the rendered block to shrink in height. - // This could happen if a cellWrap: true column has increased in width. - // It could cause the bottom of the rendered view to zoom upwards - // out of sight. - if (renderedBlockHeight < ownerContext.bodyHeight) { - if (me.viewSize >= me.store.getCount()) { - me.setBodyTop(0); - } - // Column got wider causing scroll range to shrink, leaving the view stranded above the fold. - // Scroll up to bring it into view. - else if (me.bodyTop > me.scrollTop || me.bodyTop + renderedBlockHeight < me.scrollTop + me.viewClientHeight) { - me.setBodyTop(me.scrollTop - me.trailingBufferZone * me.rowHeight); - } - } - // If the rendered block is the last lines in the dataset, - // ensure the scroll range exactly encapsuates it. - if (view.all.endIndex === (view.dataSource.getCount()) - 1) { - me.stretchView(view, me.scrollHeight = me.bodyTop + renderedBlockHeight - 1); - } - } - } - }, - refreshSize: function() { - var me = this, - view = me.view, - skipNextRefreshSize = me.skipNextRefreshSize, - dom = view.body.dom; - // We only want to skip ONE time. - me.skipNextRefreshSize = false; - if (skipNextRefreshSize || !dom) { - return; - } - // Cache the rendered block height. - me.bodyHeight = view.body.dom.offsetHeight; - // Calculates scroll range. - // Also calculates rowHeight if we do not have an own rowHeight property. - me.scrollHeight = me.getScrollHeight(); - me.stretchView(view, me.scrollHeight); - }, - /** - * Called directly from {@link Ext.view.Table#onResize}. Reacts to View changing height by - * recalculating the size of the rendered block, and either trimming it or adding to it. - * @param {Ext.view.Table} view The Table view. - * @param {Number} width The new Width. - * @param {Number} height The new height. - * @param {Number} oldWidth The old width. - * @param {Number} oldHeight The old height. - * @private - */ - onViewResize: function(view, width, height, oldWidth, oldHeight) { - var me = this, - newViewSize; - // Only process first layout (the boxready event) or height resizes. - if (!oldHeight || height !== oldHeight) { - // Recalculate the view size in rows now that the grid view has changed height - newViewSize = Math.ceil(height / me.rowHeight) + me.trailingBufferZone + me.leadingBufferZone; - me.viewSize = me.setViewSize(newViewSize); - me.viewClientHeight = view.el.dom.clientHeight; - } - }, - stretchView: function(view, scrollRange) { - var me = this; - // Ensure that both the scroll range AND the positioned view body are in the viewable area. - if (me.scrollTop > scrollRange) { - me.position = me.scrollTop = Math.max(scrollRange - me.bodyHeight, 0); - view.setScrollY(me.scrollTop); - } - if (me.bodyTop > scrollRange) { - view.body.translate(null, me.bodyTop = me.position); - } - // Tell the scroller what the scroll size is. - if (view.getScrollable()) { - me.refreshScroller(view, scrollRange); - } - }, - refreshScroller: function(view, scrollRange) { - var scroller = view.getScrollable(); - if (scroller) { - // Ensure the scroller viewport element size is up to date if it needs to be told (touch scroller) - if (scroller.setElementSize) { - scroller.setElementSize(); - } - // Ensure the scroller knows about content size - scroller.setSize({ - x: view.headerCt.getTableWidth(), - y: scrollRange - }); - } - }, - setViewSize: function(viewSize, fromLockingPartner) { - var me = this, - store = me.store, - view = me.view, - rows = view.all, - elCount = rows.getCount(), - start, end, - lockingPartner = me.view.lockingPartner && me.view.lockingPartner.bufferedRenderer, - diff = elCount - viewSize, - records, oldRows, newRows, storeCount; - // Exchange largest view size as long as the partner has been laid out (and thereby calculated a true view size) - if (lockingPartner && !fromLockingPartner && lockingPartner.view.componentLayoutCounter) { - if (lockingPartner.viewSize > viewSize) { - viewSize = lockingPartner.viewSize; - } else { - lockingPartner.setViewSize(viewSize, true); - } - } - diff = elCount - viewSize; - if (diff) { - // Must be set for getFirstVisibleRowIndex to work - me.scrollTop = view.getScrollY(); - me.viewSize = viewSize; - if (store.isBufferedStore) { - store.setViewSize(viewSize); - } - // If a store loads before we have calculated a viewSize, it loads me.defaultViewSize records. - // This may be larger or smaller than the final viewSize so the store needs adjusting when the view size is calculated. - if (elCount) { - storeCount = store.getCount(); - start = rows.startIndex; - end = Math.min(start + viewSize - 1, storeCount - 1); - // Only do expensive adding or removal if range is not already correct - if (start === rows.startIndex && end === rows.endIndex) { - // Needs rows adding to top - if (diff < 0) { - me.handleViewScroll(-1); - } - } else { - // While changing our visible range, the locking partner must not sync - if (lockingPartner) { - lockingPartner.disable(); - } - // View must expand: add rows at end if possible - if (diff < 0) { - // If it's *possible* to add rows to the end... - if (storeCount > elCount) { - // Store's getRange API always has been inclusive of endIndex. - store.getRange(rows.endIndex + 1, end, { - callback: function(records, start) { - newRows = view.doAdd(records, start); - view.fireEvent('itemadd', records, start, newRows); - me.setBodyTop(me.bodyTop); - } - }); - } else // If not possible just refresh - { - me.refreshView(0); - } - } else // View is shrinking: remove rows from end - { - // Remove the DOM rows - start = rows.endIndex - (diff - 1); - end = rows.endIndex; - oldRows = rows.slice(start, end + 1); - rows.removeRange(start, end, true); - if (view.hasListeners.itemremove) { - records = store.getRange(start, end); - view.fireEvent('itemremove', records, start, oldRows); - } - me.setBodyTop(me.bodyTop); - } - if (lockingPartner) { - lockingPartner.enable(); - } - } - } - } - return viewSize; - }, - /** - * @private - * TableView's getViewRange delegates the operation to this method if buffered rendering is present. - */ - getViewRange: function() { - var me = this, - rows = me.view.all, - store = me.store, - startIndex = 0; - // If there already is a view range, then the startIndex from that - if (rows.getCount()) { - startIndex = rows.startIndex; - } - // Otherwise use start index of current page. - // https://sencha.jira.com/browse/EXTJSIV-10724 - // Buffered store may be primed with loadPage(n) call rather than autoLoad which starts at index 0. - else if (store.isBufferedStore) { - if (!store.currentPage) { - store.currentPage = 1; - } - startIndex = rows.startIndex = (store.currentPage - 1) * (store.pageSize || 1); - // The RowNumberer uses the current page to offset the record index, so when buffered, it must always be on page 1 - store.currentPage = 1; - } - if (store.data.getCount()) { - return store.getRange(startIndex, startIndex + (me.viewSize || store.defaultViewSize) - 1); - } else { - return []; - } - }, - /** - * @private - * Handles the Store replace event, producing a correct buffered view after the replace operation. - */ - onReplace: function(store, startIndex, oldRecords, newRecords) { - var me = this, - view = me.view, - rows = view.all, - oldStartIndex, - renderedSize = rows.getCount(), - lastAffectedIndex = startIndex + oldRecords.length - 1, - recordIncrement = newRecords.length - oldRecords.length, - scrollIncrement = recordIncrement * me.rowHeight; - // All replacement activity is past the end of a full-sized rendered block; do nothing except update scroll range - if (startIndex >= rows.startIndex + me.viewSize) { - me.refreshSize(); - return; - } - // If the change is all above the rendered block and the rendered block is its maximum size, update the scroll range and - // ensure the buffer zone above is filled if possible. - if (renderedSize && lastAffectedIndex < rows.startIndex && rows.getCount() >= me.viewSize) { - // Move the index-based NodeCache up or down depending on whether it's a net adding or removal above. - rows.moveBlock(recordIncrement); - me.refreshSize(); - // If the change above us was an addition, pretend that we just scrolled upwards - // which will ensure that there is at least this.numFromEdge rows above the fold. - oldStartIndex = rows.startIndex; - if (recordIncrement > 0) { - // Do not allow this operation to mirror to the partner side. - me.doNotMirror = true; - me.handleViewScroll(-1); - me.doNotMirror = false; - } - // If the handleViewScroll did nothing, we just have to ensure the rendered block is the correct - // amount down the scroll range, and then readjust the top of the rendered block to keep the visuals the same. - if (rows.startIndex === oldStartIndex) { - // If inserting or removing invisible records above the start of the rendered block, the visible - // block must then be moved up or down the scroll range. - if (rows.startIndex) { - me.setBodyTop(me.bodyTop += scrollIncrement); - view.suspendEvent('scroll'); - view.scrollBy(0, scrollIncrement); - view.resumeEvent('scroll'); - me.position = me.scrollTop = view.getScrollY(); - } - } else // The handleViewScroll added rows, so we must scroll to keep the visuals the same; - { - view.suspendEvent('scroll'); - view.scrollBy(0, (oldStartIndex - rows.startIndex) * me.rowHeight); - view.resumeEvent('scroll'); - } - view.refreshSize(rows.getCount() !== renderedSize); - return; - } - // If the change is all below the rendered block, update the scroll range - // and ensure the buffer zone below us is filled if possible. - if (renderedSize && startIndex > rows.endIndex) { - me.refreshSize(); - // If the change below us was an addition, ask for - // rows to be rendered starting from the current startIndex. - // If more rows need to be scrolled onto the bottom of the rendered - // block to achieve this, that will do it. - if (recordIncrement > 0) { - me.onRangeFetched(null, rows.startIndex, Math.min(store.getCount(), rows.startIndex + me.viewSize) - 1, null, true); - } - view.refreshSize(rows.getCount() !== renderedSize); - return; - } - // Cut into rendered block from above - if (startIndex < rows.startIndex && lastAffectedIndex <= rows.endIndex) { - me.refreshView(rows.startIndex - oldRecords.length + newRecords.length); - return; - } - if (startIndex < rows.startIndex && lastAffectedIndex <= rows.endIndex && scrollIncrement) { - view.suspendEvent('scroll'); - view.setScrollY(me.position = me.scrollTop += scrollIncrement); - view.resumeEvent('scroll'); - } - // Only need to change display if the view is currently empty, or - // change intersects the rendered view. - me.refreshView(); - }, - /** - * @private - * Scrolls to and optionally selects the specified row index **in the total dataset**. - * - * This is a private method for internal usage by the framework. - * - * Use the grid's {@link Ext.panel.Table#ensureVisible ensureVisible} method to scroll a particular - * record or record index into view. - * - * @param {Number/Ext.data.Model} record The record, or the zero-based position in the dataset to scroll to. - * @param {Object} [options] An object containing options to modify the operation. - * @param {Boolean} [options.animate] Pass `true` to animate the row into view. - * @param {Boolean} [options.highlight] Pass `true` to highlight the row with a glow animation when it is in view. - * @param {Boolean} [options.select] Pass as `true` to select the specified row. - * @param {Boolean} [options.focus] Pass as `true` to focus the specified row. - * @param {Function} [options.callback] A function to call when the row has been scrolled to. - * @param {Number} options.callback.recordIdx The resulting record index (may have changed if the passed index was outside the valid range). - * @param {Ext.data.Model} options.callback.record The resulting record from the store. - * @param {HTMLElement} options.callback.node The resulting view row element. - * @param {Object} [options.scope] The scope (`this` reference) in which to execute the callback. Defaults to this BufferedRenderer. - * @param {Ext.grid.column.Column/Number} [options.column] The column, or column index to scroll into view. - * - */ - scrollTo: function(recordIdx, options) { - var args = arguments, - me = this, - view = me.view, - lockingPartner = view.lockingPartner && view.lockingPartner.grid.isVisible() && view.lockingPartner.bufferedRenderer, - store = me.store, - total = store.getCount(), - startIdx, endIdx, targetRow, tableTop, groupingFeature, metaGroup, record, direction; - // New option object API - if (options !== undefined && !(options instanceof Object)) { - options = { - select: args[1], - callback: args[2], - scope: args[3] - }; - } - // If we have a grouping summary feature rendering the view in groups, - // first, ensure that the record's group is expanded, - // then work out which record in the groupStore the record is at. - if ((groupingFeature = view.dataSource.groupingFeature) && (groupingFeature.collapsible)) { - if (recordIdx.isEntity) { - record = recordIdx; - } else { - record = view.store.getAt(Math.min(Math.max(recordIdx, 0), view.store.getCount() - 1)); - } - metaGroup = groupingFeature.getMetaGroup(record); - if (metaGroup && metaGroup.isCollapsed) { - if (!groupingFeature.isExpandingOrCollapsing) { - groupingFeature.expand(groupingFeature.getGroup(record).getGroupKey()); - total = store.getCount(); - recordIdx = groupingFeature.indexOf(record); - } else { - // If we've just been collapsed, then the only record we have is - // the wrapped placeholder - record = metaGroup.placeholder; - recordIdx = groupingFeature.indexOfPlaceholder(record); - } - } else { - recordIdx = groupingFeature.indexOf(record); - } - } else { - if (recordIdx.isEntity) { - record = recordIdx; - recordIdx = store.indexOf(record); - // Currently loaded pages do not contain the passed record, we cannot proceed. - if (recordIdx === -1) { - Ext.raise('Unknown record passed to BufferedRenderer#scrollTo'); - return; - } - } else { - // Sanitize the requested record index - recordIdx = Math.min(Math.max(recordIdx, 0), total - 1); - record = store.getAt(recordIdx); - } - } - // See if the required row for that record happens to be within the rendered range. - if (record && (targetRow = view.getNode(record))) { - view.grid.ensureVisible(record, options); - // Keep the view immediately replenished when we scroll an existing element into view. - // DOM scroll events fire asynchronously, and we must not leave subsequent code without a valid buffered row block. - me.onViewScroll(); - me.onViewScrollEnd(); - return; - } - // Calculate view start index. - // If the required record is above the fold... - if (recordIdx < view.all.startIndex) { - // The startIndex of the new rendered range is a little less than the target record index. - direction = -1; - startIdx = Math.max(Math.min(recordIdx - (Math.floor((me.leadingBufferZone + me.trailingBufferZone) / 2)), total - me.viewSize + 1), 0); - endIdx = Math.min(startIdx + me.viewSize - 1, total - 1); - } else // If the required record is below the fold... - { - // The endIndex of the new rendered range is a little greater than the target record index. - direction = 1; - endIdx = Math.min(recordIdx + (Math.floor((me.leadingBufferZone + me.trailingBufferZone) / 2)), total - 1); - startIdx = Math.max(endIdx - (me.viewSize - 1), 0); - } - tableTop = Math.max(startIdx * me.rowHeight, 0); - store.getRange(startIdx, endIdx, { - callback: function(range, start, end) { - // Render the range. - // Pass synchronous flag so that it does it inline, not on a timer. - // Pass fromLockingPartner flag so that it does not inform the lockingPartner. - me.renderRange(start, end, true, true); - record = store.data.getRange(recordIdx, recordIdx + 1)[0]; - targetRow = view.getNode(record); - // bodyTop property must track the translated position of the body - view.body.translate(null, me.bodyTop = tableTop); - // Ensure the scroller knows about the range if we're going down - if (direction === 1) { - me.refreshSize(); - } - // Locking partner must render the same range - if (lockingPartner) { - lockingPartner.renderRange(start, end, true, true); - // Sync all row heights - me.syncRowHeights(); - // bodyTop property must track the translated position of the body - lockingPartner.view.body.translate(null, lockingPartner.bodyTop = tableTop); - // Ensure the scroller knows about the range if we're going down - if (direction === 1) { - lockingPartner.refreshSize(); - } - } - // The target does not map to a view node. - // Cannot scroll to it. - if (!targetRow) { - return; - } - view.grid.ensureVisible(record, options); - me.scrollTop = me.position = me.view.getScrollY(); - if (lockingPartner) { - lockingPartner.position = lockingPartner.scrollTop = me.scrollTop; - } - } - }); - }, - onViewScroll: function() { - var me = this, - store = me.store, - totalCount = (store.getCount()), - vscrollDistance, scrollDirection, - scrollTop = me.scrollTop = me.view.getScrollY(); - // Only play the pointer-events;none trick on the platform it is needed on. - // WebKit does a browser layout when you change the pointer-events style. - // Stops the jagging DOM scrolling when mouse is over data rows. - if (me.needsPointerEventsFix) { - me.view.body.dom.style.pointerEvents = 'none'; - } - // Only check for nearing the edge if we are enabled, and if there is overflow beyond our view bounds. - // If there is no paging to be done (Store's dataset is all in memory) we will be disabled. - if (!(me.disabled || totalCount < me.viewSize)) { - vscrollDistance = scrollTop - me.position; - scrollDirection = vscrollDistance > 0 ? 1 : -1; - // Moved at least 20 pixels, or changed direction, so test whether the numFromEdge is triggered - if (Math.abs(vscrollDistance) >= 20 || (scrollDirection !== me.lastScrollDirection)) { - me.lastScrollDirection = scrollDirection; - me.handleViewScroll(me.lastScrollDirection); - } - } - }, - onViewScrollEnd: function() { - // Only play the pointer-events;none trick on the platform it is needed on. - // WebKit does a browser layout when you change the pointer-events style. - // Stops the jagging DOM scrolling when mouse is over data rows. - if (this.needsPointerEventsFix) { - this.view.body.dom.style.pointerEvents = ''; - } - }, - handleViewScroll: function(direction) { - var me = this, - rows = me.view.all, - store = me.store, - viewSize = me.viewSize, - lastItemIndex = store.getCount() - 1, - requestStart, requestEnd; - // We're scrolling up - if (direction === -1) { - // If table starts at record zero, we have nothing to do - if (rows.startIndex) { - if (me.topOfViewCloseToEdge()) { - requestStart = Math.max(0, me.getLastVisibleRowIndex() + me.trailingBufferZone - viewSize); - } - } - } else // We're scrolling down - { - // If table ends at last record, we have nothing to do - if (rows.endIndex < lastItemIndex) { - if (me.bottomOfViewCloseToEdge()) { - requestStart = Math.max(0, me.getFirstVisibleRowIndex() - me.trailingBufferZone); - } - } - } - // View is OK at this scroll. Advance loadId so that any load requests in flight do not - // result in rendering upon their return. - if (requestStart == null) { - // View is still valid at this scroll position. - // Do not trigger a handleViewScroll call until *ANOTHER* 20 pixels have scrolled by. - me.position = me.scrollTop; - me.loadId++; - } else // We scrolled close to the edge and the Store needs reloading - { - requestEnd = Math.min(requestStart + viewSize - 1, lastItemIndex); - // viewSize was calculated too small due to small sample row count with some skewed - // item height in there such as a tall group header item. Extend the view size in this case. - if (me.variableRowHeight && requestEnd === rows.endIndex && requestEnd < lastItemIndex) { - requestEnd++; - // Do NOT call setViewSize - that re-renders the view at the new size, - // and we are just about to scroll it to correct it. - me.viewSize = viewSize++; - if (store.isBufferedStore) { - store.setViewSize(me.viewSize); - } - } - // If calculated view range has moved, then render it and return the fact that the scroll was handled. - if (requestStart !== rows.startIndex || requestEnd !== rows.endIndex) { - me.renderRange(requestStart, requestEnd); - return true; - } - } - }, - bottomOfViewCloseToEdge: function() { - var me = this; - if (me.variableRowHeight) { - return me.bodyTop + me.bodyHeight < me.scrollTop + me.view.lastBox.height + (me.numFromEdge * me.rowHeight); - } else { - return (me.view.all.endIndex - me.getLastVisibleRowIndex()) < me.numFromEdge; - } - }, - topOfViewCloseToEdge: function() { - var me = this; - if (me.variableRowHeight) { - // The body top position is within the numFromEdge zone - return me.bodyTop > me.scrollTop - (me.numFromEdge * me.rowHeight); - } else { - return (me.getFirstVisibleRowIndex() - me.view.all.startIndex) < me.numFromEdge; - } - }, - /** - * @private - * Refreshes the current rendered range if possible. - * Optionally refreshes starting at the specified index. - */ - refreshView: function(startIndex) { - var me = this, - viewSize = me.viewSize, - rows = me.view.all, - store = me.store, - storeCount = store.getCount(), - maxIndex = Math.max(0, storeCount - 1), - endIndex; - // Empty Store is simple, don't even ask the store - if (!storeCount) { - return me.doRefreshView([], 0, 0); - } - // Store doesn't fill the required view size. Simple start/end calcs. - else if (storeCount < viewSize) { - startIndex = 0; - endIndex = maxIndex; - } else { - if (startIndex == null) { - // Use a nextRefreshStartIndex as set by a load operation in which we are maintaining scroll position - if (me.nextRefreshStartIndex != null) { - startIndex = me.nextRefreshStartIndex; - me.nextRefreshStartIndex = null; - } else { - startIndex = rows.startIndex; - } - } - // New start index should be current start index unless that's now too close to the end of the store - // to yield a full view, in which case work back from the end of the store. If working back from the end, the leading buffer zone - // cannot be rendered, so subtract it from the view size. - // Ensure we don't go negative. - startIndex = Math.max(0, Math.min(startIndex, maxIndex - (viewSize - me.leadingBufferZone) + 1)); - // New end index works forward from the new start index ensuring we don't walk off the end - endIndex = Math.min(startIndex + viewSize - 1, maxIndex); - if (endIndex - startIndex + 1 > viewSize) { - startIndex = endIndex - viewSize + 1; - } - } - if (startIndex === 0 && endIndex === 0 && storeCount === 0) { - me.doRefreshView([], 0, 0); - } else { - store.getRange(startIndex, endIndex, { - callback: me.doRefreshView, - scope: me - }); - } - }, - doRefreshView: function(range, startIndex, endIndex, options) { - var me = this, - view = me.view, - rows = view.all, - previousStartIndex = rows.startIndex, - previousEndIndex = rows.endIndex, - previousFirstItem, previousLastItem, - prevRowCount = rows.getCount(), - newNodes, - viewMoved = startIndex !== rows.startIndex, - calculatedTop, scrollIncrement, restoreFocus; - if (view.refreshCounter) { - // Give CellEditors or other transient in-cell items a chance to get out of the way. - if (view.hasListeners.beforerefresh && view.fireEvent('beforerefresh', view) === false) { - return; - } - // So that listeners to the itemremove events know that its because of a refresh. - // And so that this class's refresh listener knows to ignore it. - view.refreshing = me.refreshing = true; - // If focus was in any way in the view, whether actionable or navigable, this will return - // a function which will restore that state. - restoreFocus = view.saveFocusState(); - view.clearViewEl(true); - view.refreshCounter++; - if (range.length) { - newNodes = view.doAdd(range, startIndex); - if (viewMoved) { - // Try to find overlap between newly rendered block and old block - previousFirstItem = rows.item(previousStartIndex, true); - previousLastItem = rows.item(previousEndIndex, true); - // Work out where to move the view top if there is overlap - if (previousFirstItem) { - scrollIncrement = -previousFirstItem.offsetTop; - } else if (previousLastItem) { - scrollIncrement = rows.last(true).offsetTop - previousLastItem.offsetTop; - } - // If there was an overlap, we know exactly where to move the view - if (scrollIncrement) { - me.bodyTop = Math.max(me.bodyTop + scrollIncrement, 0); - me.scrollTop = me.bodyTop ? me.scrollTop + scrollIncrement : 0; - } else // No overlap: calculate the a new body top and scrollTop. - { - // To position rows, remove table's top border - me.bodyTop = calculatedTop = startIndex * me.rowHeight; - me.scrollTop = Math.max(calculatedTop - me.rowHeight * (calculatedTop < me.bodyTop ? me.leadingBufferZone : me.trailingBufferZone , 0)); - } - } - } else // Clearing the view. - // Ensure we jump to top. - // Apply empty text. - { - if (me.scrollTop) { - me.bodyTop = me.scrollTop = 0; - } - view.addEmptyText(); - } - // Keep scroll and rendered block positions synched. - if (viewMoved) { - me.setBodyTop(me.bodyTop); - view.suspendEvent('scroll'); - view.setScrollY(me.position = me.scrollTop); - view.resumeEvent('scroll'); - } - // Correct scroll range - me.refreshSize(); - view.refreshSize(rows.getCount() !== prevRowCount); - view.fireEvent('refresh', view, range); - // If focus was in any way in this view, this will restore it - restoreFocus(); - view.headerCt.setSortState(); - view.refreshNeeded = view.refreshing = me.refreshing = false; - } else { - view.refresh(); - } - }, - renderRange: function(start, end, forceSynchronous, fromLockingPartner) { - var me = this, - rows = me.view.all, - store = me.store; - // Skip if we are being asked to render exactly the rows that we already have. - // This can happen if the viewSize has to be recalculated (due to either a data refresh or a view resize event) - // but the calculated size ends up the same. - if (!(start === rows.startIndex && end === rows.endIndex)) { - // If range is availiable synchronously, process it now. - if (store.rangeCached(start, end)) { - me.cancelLoad(); - if (me.synchronousRender || forceSynchronous) { - me.onRangeFetched(null, start, end, null, fromLockingPartner); - } else { - if (!me.renderTask) { - me.renderTask = new Ext.util.DelayedTask(me.onRangeFetched, me, null, false); - } - // Render the new range very soon after this scroll event handler exits. - // If scrolling very quickly, a few more scroll events may fire before - // the render takes place. Each one will just *update* the arguments with which - // the pending invocation is called. - me.renderTask.delay(1, null, null, [ - null, - start, - end, - null, - fromLockingPartner - ]); - } - } else // Required range is not in the prefetch buffer. Ask the store to prefetch it. - { - me.attemptLoad(start, end); - } - } - }, - onRangeFetched: function(range, start, end, options, fromLockingPartner) { - var me = this, - view = me.view, - viewEl = view.el, - oldStart, - rows = view.all, - removeCount, - increment = 0, - calculatedTop, newTop, - lockingPartner = (view.lockingPartner && !fromLockingPartner && !me.doNotMirror) && view.lockingPartner.bufferedRenderer, - newRows, partnerNewRows, topAdditionSize, topBufferZone, i, - variableRowHeight = me.variableRowHeight, - activeEl, containsFocus, pos; - // View may have been destroyed since the DelayedTask was kicked off. - if (view.destroyed) { - return; - } - // If called as a callback from the Store, the range will be passed, if called from renderRange, it won't - if (range) { - // Re-cache the scrollTop if there has been an asynchronous call to the server. - me.scrollTop = me.view.getScrollY(); - } else { - range = me.store.getRange(start, end); - // Store may have been cleared since the DelayedTask was kicked off. - if (!range) { - return; - } - } - // If we contain focus now, but do not when we have rendered the new rows, we must focus the view el. - activeEl = Ext.Element.getActiveElement(); - containsFocus = viewEl.contains(activeEl); - // Best guess rendered block position is start row index * row height. - calculatedTop = start * me.rowHeight; - // The new range encompasses the current range. Refresh and keep the scroll position stable - if (start < rows.startIndex && end > rows.endIndex) { - // How many rows will be added at top. So that we can reposition the table to maintain scroll position - topAdditionSize = rows.startIndex - start; - // MUST use View method so that itemremove events are fired so widgets can be recycled. - view.clearViewEl(true); - newRows = view.doAdd(range, start); - view.fireEvent('itemadd', range, start, newRows); - for (i = 0; i < topAdditionSize; i++) { - increment -= newRows[i].offsetHeight; - } - // We've just added a bunch of rows to the top of our range, so move upwards to keep the row appearance stable - newTop = me.bodyTop + increment; - } else { - // No overlapping nodes, we'll need to render the whole range - // teleported flag is set in getFirstVisibleRowIndex/getLastVisibleRowIndex if - // the table body has moved outside the viewport bounds - if (me.teleported || start > rows.endIndex || end < rows.startIndex) { - newTop = calculatedTop; - // If we teleport with variable row height, the best thing is to try to render the block - // pixels above the scrollTop so that the rendered block encompasses the - // viewport. Only do that if the start is more than down the dataset. - if (variableRowHeight) { - topBufferZone = me.scrollTop < me.position ? me.leadingBufferZone : me.trailingBufferZone; - if (start > topBufferZone) { - newTop = me.scrollTop - me.rowHeight * topBufferZone; - } - } - // MUST use View method so that itemremove events are fired so widgets can be recycled. - view.clearViewEl(true); - me.teleported = false; - } - if (!rows.getCount()) { - newRows = view.doAdd(range, start); - view.fireEvent('itemadd', range, start, newRows); - } - // Moved down the dataset (content moved up): remove rows from top, add to end - else if (end > rows.endIndex) { - removeCount = Math.max(start - rows.startIndex, 0); - // We only have to bump the table down by the height of removed rows if rows are not a standard size - if (variableRowHeight) { - increment = rows.item(rows.startIndex + removeCount, true).offsetTop; - } - newRows = rows.scroll(Ext.Array.slice(range, rows.endIndex + 1 - start), 1, removeCount); - // We only have to bump the table down by the height of removed rows if rows are not a standard size - if (variableRowHeight) { - // Bump the table downwards by the height scraped off the top - newTop = me.bodyTop + increment; - } else { - newTop = calculatedTop; - } - } else // Moved up the dataset: remove rows from end, add to top - { - removeCount = Math.max(rows.endIndex - end, 0); - oldStart = rows.startIndex; - newRows = rows.scroll(Ext.Array.slice(range, 0, rows.startIndex - start), -1, removeCount); - // We only have to bump the table up by the height of top-added rows if rows are not a standard size - if (variableRowHeight) { - // Bump the table upwards by the height added to the top - newTop = me.bodyTop - rows.item(oldStart, true).offsetTop; - // We've arrived at row zero... - if (!rows.startIndex) { - // But the calculated top position is out. It must be zero at this point - // We adjust the scroll position to keep visual position of table the same. - if (newTop) { - view.setScrollY(me.position = (me.scrollTop -= newTop)); - newTop = 0; - } - } - // Not at zero yet, but the position has moved into negative range - else if (newTop < 0) { - increment = rows.startIndex * me.rowHeight; - view.setScrollY(me.position = (me.scrollTop += increment)); - newTop = me.bodyTop + increment; - } - } else { - newTop = calculatedTop; - } - } - // The position property is the scrollTop value *at which the table was last correct* - // MUST be set at table render/adjustment time - me.position = me.scrollTop; - } - // We contained focus at the start, but that activeEl has been derendered. - // Focus the cell's column header. - if (containsFocus && !viewEl.contains(activeEl)) { - pos = view.actionableMode ? view.actionPosition : view.lastFocused; - if (pos && pos.column) { - view.onFocusLeave({}); - pos.column.focus(); - } - } - // Position the item container. - newTop = Math.max(Math.floor(newTop), 0); - if (view.positionBody) { - me.setBodyTop(newTop); - } - // Sync the other side to exactly the same range from the dataset. - // Then ensure that we are still at exactly the same scroll position. - if (newRows && lockingPartner && !lockingPartner.disabled) { - // Set the pointers of the partner so that its onRangeFetched believes it is at the correct position. - lockingPartner.scrollTop = lockingPartner.position = me.scrollTop; - if (lockingPartner.view.ownerCt.isVisible()) { - partnerNewRows = lockingPartner.onRangeFetched(null, start, end, options, true); - // Sync the row heights if configured to do so, or if one side has variableRowHeight but the other doesn't. - // variableRowHeight is just a flag for the buffered rendering to know how to measure row height and - // calculate firstVisibleRow and lastVisibleRow. It does not *necessarily* mean that row heights are going - // to be asymmetric between sides. For example grouping causes variableRowHeight. But the row heights - // each side will be symmetric. - // But if one side has variableRowHeight (eg, a cellWrap: true column), and the other does not, that - // means there could be asymmetric row heights. - if (view.ownerGrid.syncRowHeight || (lockingPartner.variableRowHeight !== variableRowHeight)) { - me.syncRowHeights(newRows, partnerNewRows); - // body height might have changed with change of rows, and possible syncRowHeights call. - me.bodyHeight = view.body.dom.offsetHeight; - } - } - if (lockingPartner.bodyTop !== newTop) { - lockingPartner.setBodyTop(newTop); - } - // Set the real scrollY position after the correct data has been rendered there. - // It will not handle a scroll because the scrollTop and position have been preset. - lockingPartner.view.setScrollY(me.scrollTop); - } - return newRows; - }, - syncRowHeights: function(itemEls, partnerItemEls) { - var me = this, - ln = 0, - otherLn = 1, - // Different initial values so that all items are synched - mySynchronizer = [], - otherSynchronizer = [], - RowSynchronizer = Ext.grid.locking.RowSynchronizer, - i, rowSync; - if (itemEls && partnerItemEls) { - ln = itemEls.length; - otherLn = partnerItemEls.length; - } - // The other side might not quite by in scroll sync with us, in which case - // it may have gone a different path way and rolled some rows into - // the rendered block where we may have re-rendered the whole thing. - // If this has happened, fall back to syncing all rows. - if (ln !== otherLn) { - itemEls = me.view.all.slice(); - partnerItemEls = me.view.lockingPartner.all.slice(); - ln = otherLn = itemEls.length; - } - for (i = 0; i < ln; i++) { - mySynchronizer[i] = rowSync = new RowSynchronizer(me.view, itemEls[i]); - rowSync.measure(); - } - for (i = 0; i < otherLn; i++) { - otherSynchronizer[i] = rowSync = new RowSynchronizer(me.view.lockingPartner, partnerItemEls[i]); - rowSync.measure(); - } - for (i = 0; i < ln; i++) { - mySynchronizer[i].finish(otherSynchronizer[i]); - otherSynchronizer[i].finish(mySynchronizer[i]); - } - // Ensure that both BufferedRenderers have the same idea about scroll range and row height - me.syncRowHeightsFinish(); - }, - syncRowHeightsFinish: function() { - var me = this, - view = me.view, - lockingPartner = view.lockingPartner.bufferedRenderer; - // Now that row heights have potentially changed, both BufferedRenderers - // have to re-evaluate what they think the average rowHeight is - // based on the synchronized-height rows. - // - // If the view has not been layed out, then the upcoming first resize event - // will trigger the needed refreshSize call; See onViewRefresh - - // If control arrives there and the componentLayoutCounter is zero and - // there is variableRowHeight, it schedules itself to be run on boxready - // so refreshSize will be called there for the first time. - if (view.componentLayoutCounter) { - delete me.rowHeight; - me.refreshSize(); - if (lockingPartner.rowHeight !== me.rowHeight) { - delete lockingPartner.rowHeight; - lockingPartner.refreshSize(); - } - } - }, - setBodyTop: function(bodyTop) { - var me = this, - view = me.view, - rows = view.all, - store = me.store, - body = view.body; - if (!body.dom) { - // The view may be rendered, but the body element not attached. - return; - } - me.translateBody(body, bodyTop); - // If this is the last page, correct the scroll range to be just enough to fit. - if (me.variableRowHeight) { - me.bodyHeight = body.dom.offsetHeight; - // We are displaying the last row, so ensure the scroll range finishes exactly at the bottom of the view body - if (rows.endIndex === store.getCount() - 1) { - me.scrollHeight = bodyTop + me.bodyHeight - 1; - } else // Not last row - recalculate scroll range - { - me.scrollHeight = me.getScrollHeight(); - } - me.stretchView(view, me.scrollHeight); - } else { - // If we have fixed row heights, calculate rendered block height without forcing a layout - me.bodyHeight = rows.getCount() * me.rowHeight; - } - }, - translateBody: function(body, bodyTop) { - body.translate(null, this.bodyTop = bodyTop); - }, - getFirstVisibleRowIndex: function(startRow, endRow, viewportTop, viewportBottom) { - var me = this, - view = me.view, - rows = view.all, - elements = rows.elements, - clientHeight = me.viewClientHeight, - target, targetTop, - bodyTop = me.bodyTop; - // If variableRowHeight, we have to search for the first row who's bottom edge is within the viewport - if (rows.getCount() && me.variableRowHeight) { - if (!arguments.length) { - startRow = rows.startIndex; - endRow = rows.endIndex; - viewportTop = me.scrollTop; - viewportBottom = viewportTop + clientHeight; - // Teleported so that body is outside viewport: Use rowHeight calculation - if (bodyTop > viewportBottom || bodyTop + me.bodyHeight < viewportTop) { - me.teleported = true; - return Math.floor(me.scrollTop / me.rowHeight); - } - // In first, non-recursive call, begin targeting the most likely first row - target = startRow + Math.min(me.numFromEdge + ((me.lastScrollDirection === -1) ? me.leadingBufferZone : me.trailingBufferZone), Math.floor((endRow - startRow) / 2)); - } else { - target = startRow + Math.floor((endRow - startRow) / 2); - } - targetTop = bodyTop + elements[target].offsetTop; - // If target is entirely above the viewport, chop downwards - if (targetTop + elements[target].offsetHeight <= viewportTop) { - return me.getFirstVisibleRowIndex(target + 1, endRow, viewportTop, viewportBottom); - } - // Target is first - if (targetTop <= viewportTop) { - return target; - } - // Not narrowed down to 1 yet; chop upwards - else if (target !== startRow) { - return me.getFirstVisibleRowIndex(startRow, target - 1, viewportTop, viewportBottom); - } - } - return Math.floor(me.scrollTop / me.rowHeight); - }, - /** - * Returns the index of the last row in your table view deemed to be visible. - * @return {Number} - * @private - */ - getLastVisibleRowIndex: function(startRow, endRow, viewportTop, viewportBottom) { - var me = this, - view = me.view, - rows = view.all, - elements = rows.elements, - clientHeight = me.viewClientHeight, - target, targetTop, targetBottom, - bodyTop = me.bodyTop; - // If variableRowHeight, we have to search for the first row who's bottom edge is below the bottom of the viewport - if (rows.getCount() && me.variableRowHeight) { - if (!arguments.length) { - startRow = rows.startIndex; - endRow = rows.endIndex; - viewportTop = me.scrollTop; - viewportBottom = viewportTop + clientHeight; - // Teleported so that body is outside viewport: Use rowHeight calculation - if (bodyTop > viewportBottom || bodyTop + me.bodyHeight < viewportTop) { - me.teleported = true; - return Math.floor(me.scrollTop / me.rowHeight) + Math.ceil(clientHeight / me.rowHeight); - } - // In first, non-recursive call, begin targeting the most likely last row - target = endRow - Math.min(me.numFromEdge + ((me.lastScrollDirection === 1) ? me.leadingBufferZone : me.trailingBufferZone), Math.floor((endRow - startRow) / 2)); - } else { - target = startRow + Math.floor((endRow - startRow) / 2); - } - targetTop = bodyTop + elements[target].offsetTop; - // If target is entirely below the viewport, chop upwards - if (targetTop > viewportBottom) { - return me.getLastVisibleRowIndex(startRow, target - 1, viewportTop, viewportBottom); - } - targetBottom = targetTop + elements[target].offsetHeight; - // Target is last - if (targetBottom >= viewportBottom) { - return target; - } - // Not narrowed down to 1 yet; chop downwards - else if (target !== endRow) { - return me.getLastVisibleRowIndex(target + 1, endRow, viewportTop, viewportBottom); - } - } - return me.getFirstVisibleRowIndex() + Math.ceil(clientHeight / me.rowHeight); - }, - getScrollHeight: function() { - var me = this, - view = me.view, - rows = view.all, - store = me.store, - recCount = store.getCount(), - rowCount = rows.getCount(), - row, rowHeight, borderWidth, scrollHeight; - if (!recCount) { - return 0; - } - if (!me.hasOwnProperty('rowHeight')) { - if (rowCount) { - if (me.variableRowHeight) { - me.rowHeight = Math.floor(me.bodyHeight / rowCount); - } else { - row = rows.first(); - rowHeight = row.getHeight(); - // In IE8 we're adding bottom border on all the rows to work around - // the lack of :last-child selector, and we compensate that by setting - // a negative top margin that equals the border width, so that top and - // bottom borders overlap on adjacent rows. Negative margin does not - // affect the row's reported height though so we have to compensate - // for that effectively invisible additional border width here. - if (Ext.isIE8) { - borderWidth = row.getBorderWidth('b'); - if (borderWidth > 0) { - rowHeight -= borderWidth; - } - } - me.rowHeight = rowHeight; - } - } else { - delete me.rowHeight; - } - } - if (me.variableRowHeight) { - // If this is the last page, ensure the scroll range is exactly enough to scroll to the end of the rendered block. - if (rows.endIndex === recCount - 1) { - scrollHeight = me.bodyTop + me.bodyHeight - 1; - } else // Calculate the scroll range based upon measured row height and our scrollPosition. - { - scrollHeight = Math.floor((recCount - rowCount) * me.rowHeight) + me.bodyHeight; - // If there's a discrepancy between the boy position we have scrolled to, and the calculated position, - // account for that in the scroll range so that we have enough range to scroll all the data into view. - scrollHeight += me.bodyTop - rows.startIndex * me.rowHeight; - } - } else { - scrollHeight = Math.floor(recCount * me.rowHeight); - } - return (me.scrollHeight = scrollHeight); - }, - // jshint ignore:line - attemptLoad: function(start, end) { - var me = this; - if (me.scrollToLoadBuffer) { - if (!me.loadTask) { - me.loadTask = new Ext.util.DelayedTask(me.doAttemptLoad, me, []); - } - me.loadTask.delay(me.scrollToLoadBuffer, me.doAttemptLoad, me, [ - start, - end - ]); - } else { - me.doAttemptLoad(start, end); - } - }, - cancelLoad: function() { - if (this.loadTask) { - this.loadTask.cancel(); - } - }, - doAttemptLoad: function(start, end) { - var me = this; - // If we were called on a delay, check for destruction - if (!me.destroyed) { - me.store.getRange(start, end, { - loadId: ++me.loadId, - callback: function(range, start, end, options) { - // If our loadId position has not changed since the getRange request started, we can continue to render - if (options.loadId === me.loadId) { - me.onRangeFetched(range, start, end, options); - } - }, - fireEvent: false - }); - } - }, - destroy: function() { - var me = this, - view = me.view; - me.cancelLoad(); - if (view && view.el) { - view.un('scroll', me.onViewScroll, me); - } - if (me.store) { - me.unbindStore(); - } - // Remove listeners from old grid, view and store - me.viewListeners = me.gridListeners = me.view = me.grid = Ext.destroy(me.viewListeners, me.stretcher, me.gridListeners); - me.callParent(); - } -}, function(cls) { - // Minimal leading and trailing zones are best on mobile. - // Use 2 to ensure visible range is covered - if (Ext.supports.Touch) { - cls.prototype.leadingBufferZone = cls.prototype.trailingBufferZone = 2; - cls.prototype.numFromEdge = 1; - } -}); - -/** - * This class provides an abstract grid editing plugin on selected {@link Ext.grid.column.Column columns}. - * The editable columns are specified by providing an {@link Ext.grid.column.Column#editor editor} - * in the {@link Ext.grid.column.Column column configuration}. - * - * **Note:** This class should not be used directly. See {@link Ext.grid.plugin.CellEditing} and - * {@link Ext.grid.plugin.RowEditing}. - */ -Ext.define('Ext.grid.plugin.Editing', { - extend: 'Ext.plugin.Abstract', - alias: 'editing.editing', - requires: [ - 'Ext.grid.column.Column', - 'Ext.util.KeyNav', - // Requiring Ext.form.field.Base and Ext.view.Table ensures that grid editor sass - // variables can derive from both form field vars and grid vars in the neutral theme - 'Ext.form.field.Base', - 'Ext.view.Table' - ], - mixins: [ - 'Ext.mixin.Observable' - ], - /** - * @cfg {Number} clicksToEdit - * The number of clicks on a grid required to display the editor. - * The only accepted values are **1** and **2**. - */ - clicksToEdit: 2, - /** - * @cfg {String} triggerEvent - * The event which triggers editing. Supersedes the {@link #clicksToEdit} configuration. May be one of: - * - * * cellclick - * * celldblclick - * * cellfocus - * * rowfocus - */ - triggerEvent: undefined, - /** - * @property {Boolean} editing - * Set to `true` while the editing plugin is active and an Editor is visible. - */ - relayedEvents: [ - 'beforeedit', - 'edit', - 'validateedit', - 'canceledit' - ], - /** - * @cfg {String} default UI for editor fields - */ - defaultFieldUI: 'default', - // @private - defaultFieldXType: 'textfield', - // cell, row, form - editStyle: '', - /** - * @event beforeedit - * Fires before editing is triggered. Return false from event handler to stop the editing. - * - * @param {Ext.grid.plugin.Editing} editor - * @param {Object} context The editing context with the following properties: - * @param {Ext.grid.Panel} context.grid The owning grid Panel. - * @param {Ext.data.Model} context.record The record being edited. - * @param {String} context.field The name of the field being edited. - * @param {Mixed} context.value The field's current value. - * @param {HTMLElement} context.row The grid row element. - * @param {Ext.grid.column.Column} context.column The Column being edited. - * @param {Number} context.rowIdx The index of the row being edited. - * @param {Number} context.colIdx The index of the column being edited. - * @param {Boolean} context.cancel Set this to `true` to cancel the edit or return false from your handler. - * @param {Mixed} context.originalValue Alias for value (only when using {@link Ext.grid.plugin.CellEditing CellEditing}). - */ - /** - * @event edit - * Fires after editing. Usage example: - * - * grid.on('edit', function(editor, e) { - * // commit the changes right after editing finished - * e.record.commit(); - * }); - * - * @param {Ext.grid.plugin.Editing} editor - * @param {Object} context The editing context with the following properties: - * @param {Ext.grid.Panel} context.grid The owning grid Panel. - * @param {Ext.data.Model} context.record The record being edited. - * @param {String} context.field The name of the field being edited. - * @param {Mixed} context.value The field's current value. - * @param {HTMLElement} context.row The grid row element. - * @param {Ext.grid.column.Column} context.column The Column being edited. - * @param {Number} context.rowIdx The index of the row being edited. - * @param {Number} context.colIdx The index of the column being edited. - */ - /** - * @event validateedit - * Fires after editing, but before the value is set in the record. Return false from event handler to - * cancel the change. - * - * Usage example showing how to remove the red triangle (dirty record indicator) from some records (not all). By - * observing the grid's validateedit event, it can be cancelled if the edit occurs on a targeted row (for example) - * and then setting the field's new value in the Record directly: - * - * grid.on('validateedit', function (editor, context) { - * var myTargetRow = 6; - * - * if (context.rowIdx === myTargetRow) { - * context.record.data[context.field] = context.value; - * } - * }); - * - * @param {Ext.grid.plugin.Editing} editor - * @param {Object} context The editing context with the following properties: - * @param {Ext.grid.Panel} context.grid The owning grid Panel. - * @param {Ext.data.Model} context.record The record being edited. - * @param {String} context.field The name of the field being edited. - * @param {Mixed} context.value The field's current value. - * @param {HTMLElement} context.row The grid row element. - * @param {Ext.grid.column.Column} context.column The Column being edited. - * @param {Number} context.rowIdx The index of the row being edited. - * @param {Number} context.colIdx The index of the column being edited. - */ - /** - * @event canceledit - * Fires when the user started editing but then cancelled the edit. - * @param {Ext.grid.plugin.Editing} editor - * @param {Object} context The editing context with the following properties: - * @param {Ext.grid.Panel} context.grid The owning grid Panel. - * @param {Ext.data.Model} context.record The record being edited. - * @param {String} context.field The name of the field being edited. - * @param {Mixed} context.value The field's current value. - * @param {HTMLElement} context.row The grid row element. - * @param {Ext.grid.column.Column} context.column The Column being edited. - * @param {Number} context.rowIdx The index of the row being edited. - * @param {Number} context.colIdx The index of the column being edited. - */ - constructor: function(config) { - var me = this; - me.callParent([ - config - ]); - me.mixins.observable.constructor.call(me); - // TODO: Deprecated, remove in 5.0 - me.on("edit", function(editor, e) { - me.fireEvent("afteredit", editor, e); - }); - }, - // @private - init: function(grid) { - var me = this, - ownerLockable = grid.ownerLockable; - me.grid = grid; - me.view = grid.view; - me.initEvents(); - // Set up fields at render and reconfigure time - if (grid.rendered) { - me.setup(); - } else { - me.mon(grid, { - beforereconfigure: me.onBeforeReconfigure, - reconfigure: me.onReconfigure, - scope: me, - beforerender: { - fn: me.onBeforeRender, - single: true, - scope: me - } - }); - } - grid.editorEventRelayers = grid.relayEvents(me, me.relayedEvents); - // If the editable grid is owned by a lockable, relay up another level. - if (ownerLockable) { - ownerLockable.editorEventRelayers = ownerLockable.relayEvents(me, me.relayedEvents); - } - // Marks the grid as editable, so that the SelectionModel - // can make appropriate decisions during navigation - grid.isEditable = true; - grid.editingPlugin = grid.view.editingPlugin = me; - }, - onBeforeReconfigure: function() { - this.reconfiguring = true; - }, - /** - * Fires after the grid is reconfigured - * @protected - */ - onReconfigure: function() { - this.setup(); - delete this.reconfiguring; - }, - onBeforeRender: function() { - this.setup(); - }, - setup: function() { - // In a Lockable assembly, the owner's view aggregates all grid columns across both sides. - // We grab all columns here. - this.initFieldAccessors(this.grid.getTopLevelColumnManager().getColumns()); - }, - destroy: function() { - var me = this, - grid = me.grid; - Ext.destroy(me.keyNav); - // Clear all listeners from all our events, clear all managed listeners we added to other Observables - me.clearListeners(); - if (grid) { - if (grid.ownerLockable) { - Ext.destroy(grid.ownerLockable.editorEventRelayers); - grid.ownerLockable.editorEventRelayers = null; - } - Ext.destroy(grid.editorEventRelayers); - grid.editorEventRelayers = null; - grid.editingPlugin = grid.view.editingPlugin = me.grid = me.view = me.editor = me.keyNav = null; - } - me.callParent(); - }, - // @private - getEditStyle: function() { - return this.editStyle; - }, - // @private - initFieldAccessors: function(columns) { - // If we have been passed a group header, process its leaf headers - if (columns.isGroupHeader) { - columns = columns.getGridColumns(); - } - // Ensure we are processing an array - else if (!Ext.isArray(columns)) { - columns = [ - columns - ]; - } - var me = this, - c, - cLen = columns.length, - getEditor = function(record, defaultField) { - return me.getColumnField(this, defaultField); - }, - hasEditor = function() { - return me.hasColumnField(this); - }, - setEditor = function(field) { - me.setColumnField(this, field); - }, - column; - for (c = 0; c < cLen; c++) { - column = columns[c]; - if (!column.getEditor) { - column.getEditor = getEditor; - } - if (!column.hasEditor) { - column.hasEditor = hasEditor; - } - if (!column.setEditor) { - column.setEditor = setEditor; - } - } - }, - // @private - removeFieldAccessors: function(columns) { - // If we have been passed a group header, process its leaf headers - if (columns.isGroupHeader) { - columns = columns.getGridColumns(); - } - // Ensure we are processing an array - else if (!Ext.isArray(columns)) { - columns = [ - columns - ]; - } - var c, - cLen = columns.length, - column; - for (c = 0; c < cLen; c++) { - column = columns[c]; - column.getEditor = column.hasEditor = column.setEditor = column.field = column.editor = null; - } - }, - // @private - // remaps to the public API of Ext.grid.column.Column.getEditor - getColumnField: function(columnHeader, defaultField) { - var me = this, - field = columnHeader.field; - if (!(field && field.isFormField)) { - field = columnHeader.field = me.createColumnField(columnHeader, defaultField); - } - if (field && field.ui === 'default' && !field.hasOwnProperty('ui')) { - field.ui = me.defaultFieldUI; - } - return field; - }, - // @private - // remaps to the public API of Ext.grid.column.Column.hasEditor - hasColumnField: function(columnHeader) { - return !!(columnHeader.field && columnHeader.field.isComponent); - }, - // @private - // remaps to the public API of Ext.grid.column.Column.setEditor - setColumnField: function(columnHeader, field) { - columnHeader.field = field; - columnHeader.field = this.createColumnField(columnHeader); - }, - createColumnField: function(column, defaultField) { - var field = column.field, - dataIndex; - if (!field && column.editor) { - field = column.editor; - column.editor = null; - } - if (!field && defaultField) { - field = defaultField; - } - if (field) { - dataIndex = column.dataIndex; - if (field.isComponent) { - field.column = column; - } else { - if (Ext.isString(field)) { - field = { - name: dataIndex, - xtype: field, - column: column - }; - } else { - field = Ext.apply({ - name: dataIndex, - column: column - }, field); - } - field = Ext.ComponentManager.create(field, this.defaultFieldXType); - } - // Stamp on the dataIndex which will serve as a reliable lookup regardless - // of how the editor was defined (as a config or as an existing component). - // See EXTJSIV-11650. - field.dataIndex = dataIndex; - field.isEditorComponent = true; - column.field = field; - } - return field; - }, - // @private - initEvents: function() { - var me = this; - me.initEditTriggers(); - me.initCancelTriggers(); - }, - // @abstract - initCancelTriggers: Ext.emptyFn, - // @private - initEditTriggers: function() { - var me = this, - view = me.view; - // Listen for the edit trigger event. - if (me.triggerEvent === 'cellfocus') { - me.mon(view, 'cellfocus', me.onCellFocus, me); - } else if (me.triggerEvent === 'rowfocus') { - me.mon(view, 'rowfocus', me.onRowFocus, me); - } else { - // Prevent the View from processing when the SelectionModel focuses. - // This is because the SelectionModel processes the mousedown event, and - // focusing causes a scroll which means that the subsequent mouseup might - // take place at a different document XY position, and will therefore - // not trigger a click. - // This Editor must call the View's focusCell method directly when we recieve a request to edit - if (view.getSelectionModel().isCellModel) { - view.onCellFocus = me.beforeViewCellFocus.bind(me); - } - // Listen for whichever click event we are configured to use - me.mon(view, me.triggerEvent || ('cell' + (me.clicksToEdit === 1 ? 'click' : 'dblclick')), me.onCellClick, me); - } - // add/remove header event listeners need to be added immediately because - // columns can be added/removed before render - me.initAddRemoveHeaderEvents(); - // Attach new bindings to the View's NavigationModel which processes cellkeydown events. - me.view.getNavigationModel().addKeyBindings({ - esc: me.onEscKey, - scope: me - }); - }, - // Override of View's method so that we can pre-empt the View's processing if the view is being triggered by a mousedown - beforeViewCellFocus: function(position) { - // Pass call on to view if the navigation is from the keyboard, or we are not going to edit this cell. - if (this.view.selModel.keyNavigation || !this.editing || !this.isCellEditable || !this.isCellEditable(position.row, position.columnHeader)) { - this.view.focusCell.apply(this.view, arguments); - } - }, - // @private Used if we are triggered by the rowfocus event - onRowFocus: function(record, row, rowIdx) { - this.startEdit(row, 0); - }, - // @private Used if we are triggered by the cellfocus event - onCellFocus: function(record, cell, position) { - this.startEdit(position.row, position.column); - }, - // @private Used if we are triggered by a cellclick event - // *IMPORTANT* Due to V4.0.0 history, the colIdx here is the index within ALL columns, including hidden. - onCellClick: function(view, cell, colIdx, record, row, rowIdx, e) { - // Make sure that the column has an editor. In the case of CheckboxModel, - // calling startEdit doesn't make sense when the checkbox is clicked. - // Also, cancel editing if the element that was clicked was a tree expander. - var expanderSelector = view.expanderSelector, - // Use getColumnManager() in this context because colIdx includes hidden columns. - columnHeader = view.ownerCt.getColumnManager().getHeaderAtIndex(colIdx), - editor = columnHeader.getEditor(record); - if (this.shouldStartEdit(editor) && (!expanderSelector || !e.getTarget(expanderSelector))) { - view.ownerGrid.setActionableMode(true, e.position); - } - }, - initAddRemoveHeaderEvents: function() { - var me = this, - headerCt = me.grid.headerCt; - me.mon(headerCt, { - scope: me, - add: me.onColumnAdd, - columnmove: me.onColumnMove, - beforedestroy: me.beforeGridHeaderDestroy - }); - }, - // @private - onColumnAdd: function(ct, column) { - this.initFieldAccessors(column); - }, - // Template method which may be implemented in subclasses (RowEditing and CellEditing) - onColumnMove: Ext.emptyFn, - // @private - onEscKey: function(e) { - if (this.editing) { - var targetComponent = Ext.getCmp(e.getTarget().getAttribute('componentId')); - // ESCAPE when a picker is expanded does not cancel the edit - if (!(targetComponent && targetComponent.isPickerField && targetComponent.isExpanded)) { - return this.cancelEdit(); - } - } - }, - /** - * @method - * @private - * @template - * Template method called before editing begins. - * @param {Object} context The current editing context - * @return {Boolean} Return false to cancel the editing process - */ - beforeEdit: Ext.emptyFn, - shouldStartEdit: function(editor) { - return !!editor; - }, - /** - * @private - * Collects all information necessary for any subclasses to perform their editing functions. - * @param {Ext.data.Model/Number} record The record or record index to edit. - * @param {Ext.grid.column.Column/Number} columnHeader The column of column index to edit. - * @return {Ext.grid.CellContext/undefined} The editing context based upon the passed record and column - */ - getEditingContext: function(record, columnHeader) { - var me = this, - grid = me.grid, - colMgr = grid.visibleColumnManager, - view, gridRow, rowIdx, colIdx, result, - layoutView = me.grid.lockable ? me.grid : me.view; - // The view must have had a layout to show the editor correctly, defer until that time. - // In case a grid's startup code invokes editing immediately. - if (!layoutView.componentLayoutCounter) { - layoutView.on({ - boxready: Ext.Function.bind(me.startEdit, me, [ - record, - columnHeader - ]), - single: true - }); - return; - } - // If disabled or grid collapsed, or view not truly visible, don't calculate a context - we cannot edit - if (me.disabled || me.grid.collapsed || !me.grid.view.isVisible(true)) { - return; - } - // They've asked to edit by column number. - // Note that in a locked grid, the columns are enumerated in a unified set for this purpose. - if (Ext.isNumber(columnHeader)) { - columnHeader = colMgr.getHeaderAtIndex(columnHeader); - } - // No corresponding column. Possible if all columns have been moved to the other side of a lockable grid pair - if (!columnHeader) { - return; - } - // Coerce the column to the closest visible column - if (columnHeader.hidden) { - columnHeader = columnHeader.next(':not([hidden])') || columnHeader.prev(':not([hidden])'); - } - // Navigate to the view and grid which the column header relates to. - view = columnHeader.getView(); - grid = view.ownerCt; - // Ensure the row we want to edit is in the rendered range if the view is buffer rendered - grid.ensureVisible(record, { - column: columnHeader - }); - gridRow = view.getRow(record); - // An intervening listener may have deleted the Record. - if (!gridRow) { - return; - } - // Column index must be relative to the View the Context is using. - // It must be the real owning View, NOT the lockable pseudo view. - colIdx = view.getVisibleColumnManager().indexOf(columnHeader); - if (Ext.isNumber(record)) { - // look up record if numeric row index was passed - rowIdx = record; - record = view.getRecord(gridRow); - } else { - rowIdx = view.indexOf(gridRow); - } - // The record may be removed from the store but the view - // not yet updated, so check it exists - if (!record) { - return; - } - // Create a new CellContext - result = new Ext.grid.CellContext(view).setAll(view, rowIdx, colIdx, record, columnHeader); - // Add extra Editing information - result.grid = grid; - result.store = view.dataSource; - result.field = columnHeader.dataIndex; - result.value = result.originalValue = record.get(columnHeader.dataIndex); - result.row = gridRow; - result.node = view.getNode(record); - result.cell = view.getCellByPosition(result, true); - return result; - }, - /** - * Cancels any active edit that is in progress. - */ - cancelEdit: function() { - var me = this; - me.editing = false; - me.fireEvent('canceledit', me, me.context); - }, - /** - * Completes the edit if there is an active edit in progress. - */ - completeEdit: function() { - var me = this; - if (me.editing && me.validateEdit()) { - me.fireEvent('edit', me, me.context); - } - me.context = null; - me.editing = false; - }, - // @abstract - validateEdit: function(context) { - var me = this; - return me.fireEvent('validateedit', me, context) !== false && !context.cancel; - } -}); - -/** - * The Ext.grid.plugin.CellEditing plugin injects editing at a cell level for a Grid. Only a single - * cell will be editable at a time. The field that will be used for the editor is defined at the - * {@link Ext.grid.column.Column#editor editor}. The editor can be a field instance or a field configuration. - * - * If an editor is not specified for a particular column then that cell will not be editable and it will - * be skipped when activated via the mouse or the keyboard. - * - * The editor may be shared for each column in the grid, or a different one may be specified for each column. - * An appropriate field type should be chosen to match the data structure that it will be editing. For example, - * to edit a date, it would be useful to specify {@link Ext.form.field.Date} as the editor. - * - * ## Example - * - * A grid with editor for the name and the email columns: - * - * @example - * Ext.create('Ext.data.Store', { - * storeId: 'simpsonsStore', - * fields:[ 'name', 'email', 'phone'], - * data: [ - * { name: 'Lisa', email: 'lisa@simpsons.com', phone: '555-111-1224' }, - * { name: 'Bart', email: 'bart@simpsons.com', phone: '555-222-1234' }, - * { name: 'Homer', email: 'homer@simpsons.com', phone: '555-222-1244' }, - * { name: 'Marge', email: 'marge@simpsons.com', phone: '555-222-1254' } - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Simpsons', - * store: Ext.data.StoreManager.lookup('simpsonsStore'), - * columns: [ - * {header: 'Name', dataIndex: 'name', editor: 'textfield'}, - * {header: 'Email', dataIndex: 'email', flex:1, - * editor: { - * xtype: 'textfield', - * allowBlank: false - * } - * }, - * {header: 'Phone', dataIndex: 'phone'} - * ], - * selModel: 'cellmodel', - * plugins: { - * ptype: 'cellediting', - * clicksToEdit: 1 - * }, - * height: 200, - * width: 400, - * renderTo: Ext.getBody() - * }); - * - * This requires a little explanation. We're passing in `store` and `columns` as normal, but - * we also specify a {@link Ext.grid.column.Column#field field} on two of our columns. For the - * Name column we just want a default textfield to edit the value, so we specify 'textfield'. - * For the Email column we customized the editor slightly by passing allowBlank: false, which - * will provide inline validation. - * - * To support cell editing, we also specified that the grid should use the 'cellmodel' - * {@link Ext.grid.Panel#selModel selModel}, and created an instance of the CellEditing plugin, - * which we configured to activate each editor after a single click. - * - */ -Ext.define('Ext.grid.plugin.CellEditing', { - alias: 'plugin.cellediting', - extend: 'Ext.grid.plugin.Editing', - requires: [ - 'Ext.grid.CellEditor', - 'Ext.util.DelayedTask' - ], - /** - * @event beforeedit - * Fires before cell editing is triggered. Return false from event handler to stop the editing. - * - * @param {Ext.grid.plugin.CellEditing} editor - * @param {Object} context An editing context event with the following properties: - * @param {Ext.grid.Panel} context.grid The owning grid Panel. - * @param {Ext.data.Model} context.record The record being edited. - * @param {String} context.field The name of the field being edited. - * @param {Mixed} context.value The field's current value. - * @param {HTMLElement} context.row The grid row element. - * @param {Ext.grid.column.Column} context.column The {@link Ext.grid.column.Column} Column} being edited. - * @param {Number} context.rowIdx The index of the row being edited. - * @param {Number} context.colIdx The index of the column being edited. - * @param {Boolean} context.cancel Set this to `true` to cancel the edit or return false from your handler. - */ - /** - * @event edit - * Fires after a cell is edited. Usage example: - * - * grid.on('edit', function(editor, e) { - * // commit the changes right after editing finished - * e.record.commit(); - * }); - * - * @param {Ext.grid.plugin.CellEditing} editor - * @param {Object} context An editing context with the following properties: - * @param {Ext.grid.Panel} context.grid The owning grid Panel. - * @param {Ext.data.Model} context.record The record being edited. - * @param {String} context.field The name of the field being edited. - * @param {Mixed} context.value The field's current value. - * @param {HTMLElement} context.row The grid row element. - * @param {Ext.grid.column.Column} context.column The {@link Ext.grid.column.Column} Column} being edited. - * @param {Number} context.rowIdx The index of the row being edited. - * @param {Number} context.colIdx The index of the column being edited. - * @param {Mixed} context.originalValue The original value before being edited. - */ - /** - * @event validateedit - * Fires after a cell is edited, but before the value is set in the record. Return false from event handler to - * cancel the change. - * - * Usage example showing how to remove the red triangle (dirty record indicator) from some records (not all). By - * observing the grid's validateedit event, it can be cancelled if the edit occurs on a targeted row (for - * example) and then setting the field's new value in the Record directly: - * - * grid.on('validateedit', function(editor, e) { - * var myTargetRow = 6; - * - * if (e.row == myTargetRow) { - * e.cancel = true; - * e.record.data[e.field] = e.value; - * } - * }); - * - * @param {Ext.grid.plugin.CellEditing} editor - * @param {Object} context An editing context with the following properties: - * @param {Ext.grid.Panel} context.grid The owning grid Panel. - * @param {Ext.data.Model} context.record The record being edited. - * @param {String} context.field The name of the field being edited. - * @param {Mixed} context.value The field's current value. - * @param {HTMLElement} context.row The grid row element. - * @param {Ext.grid.column.Column} context.column The {@link Ext.grid.column.Column} Column} being edited. - * @param {Number} context.rowIdx The index of the row being edited. - * @param {Number} context.colIdx The index of the column being edited. - * @param {Mixed} context.originalValue The original value before being edited. - * @param {Boolean} context.cancel Set this to `true` to cancel the edit or return false from your handler. - */ - /** - * @event canceledit - * Fires when the user started editing a cell but then cancelled the edit. - * @param {Ext.grid.plugin.CellEditing} editor - * @param {Object} context An edit event with the following properties: - * @param {Ext.grid.Panel} context.grid The owning grid Panel. - * @param {Ext.data.Model} context.record The record being edited. - * @param {String} context.field The name of the field being edited. - * @param {Mixed} context.value The field's current value. - * @param {HTMLElement} context.row The grid row element. - * @param {Ext.grid.column.Column} context.column The {@link Ext.grid.column.Column} Column} being edited. - * @param {Number} context.rowIdx The index of the row being edited. - * @param {Number} context.colIdx The index of the column being edited. - * @param {Mixed} context.originalValue The original value before being edited. - */ - init: function(grid) { - var me = this; - // Upon deactivation, all editors are hidden. - // This will terminate their edit. - // But we do not know what further mutation the DOM grid may undergo, so after having - // given the focus events to run their course, we cache the editors in the detached body - // to prevent them from being garbage collected in case they get tipped out of the DOM - // by a view refresh. - me.cacheDeactivatedEditors = Ext.Function.createAnimationFrame(me.cacheDeactivatedEditors); - // This plugin has an interest in entering actionable mode. - // It places the cell editors into the tabbable flow. - grid.registerActionable(me); - me.callParent(arguments); - me.editors = new Ext.util.MixedCollection(false, function(editor) { - return editor.editorId; - }); - }, - // Ensure editors are cleaned up. - beforeGridHeaderDestroy: function(headerCt) { - var me = this, - columns = me.grid.getColumnManager().getColumns(), - len = columns.length, - i, column, editor; - for (i = 0; i < len; i++) { - column = columns[i]; - // Try to get the CellEditor which contains the field to destroy the whole assembly - editor = me.editors.getByKey(column.getItemId()); - // Failing that, the field has not yet been accessed to add to the CellEditor, but must still be destroyed - if (!editor) { - // If we have an editor, it will wrap the field which will be destroyed. - editor = column.editor || column.field; - } - // Destroy the CellEditor or field - Ext.destroy(editor); - me.removeFieldAccessors(column); - } - }, - onReconfigure: function(grid, store, columns) { - // Only reconfigure editors if passed a new set of columns - if (columns) { - this.editors.clear(); - } - this.callParent(); - }, - destroy: function() { - var me = this; - if (me.editors) { - me.editors.each(Ext.destroy, Ext); - me.editors.clear(); - } - me.callParent(); - }, - /** - * @private - * Template method called from the base class's initEvents - */ - initCancelTriggers: function() { - var me = this, - grid = me.grid; - me.mon(grid, { - columnresize: me.cancelEdit, - columnmove: me.cancelEdit, - scope: me - }); - }, - isCellEditable: function(record, columnHeader) { - var me = this, - context = me.getEditingContext(record, columnHeader); - if (context.view.isVisible(true) && context) { - columnHeader = context.column; - record = context.record; - if (columnHeader && me.getEditor(record, columnHeader)) { - return true; - } - } - }, - /** - * This method is called when actionable mode is requested for a cell. - * @param {Ext.grid.CellContext} position The position at which actionable mode was requested. - * @return {Boolean} `true` if this cell is actionable (editable) - * @protected - */ - activateCell: function(position) { - var me = this, - record = position.record, - column = position.column, - context, cell, editor; - context = me.getEditingContext(record, column); - if (!context) { - return; - } - if (!me.preventBeforeCheck) { - // Allow vetoing, or setting a new editor *before* we call getEditor - if (!column.getEditor(record) || me.beforeEdit(context) === false || me.fireEvent('beforeedit', me, context) === false || context.cancel) { - return; - } - } - // Recapture the editor. The beforeedit listener is allowed to replace the field. - editor = me.getEditor(record, column); - if (editor) { - cell = Ext.get(context.cell); - // Ensure editor is there in the cell, but hidden. - // It will show if it begins editing. - // And will then be found in the tabbable children of the activating cell - if (!editor.rendered) { - editor.hidden = true; - editor.render(cell, 0); - } else { - if (editor.container !== cell) { - editor.container = cell; - cell.dom.insertBefore(editor.el.dom, cell.dom.firstChild); - } - editor.hide(); - } - me.setEditingContext(context); - // Request that the editor start. - // It may veto, and return with the editing flag false. - editor.startEdit(cell, context.value, false); - // Set contextual information if we began editing (can be vetoed by events) - if (editor.editing) { - me.setActiveEditor(editor); - me.setActiveRecord(record); - me.setActiveColumn(column); - me.editing = true; - me.scroll = position.view.el.getScroll(); - } - // Return true if the cell is actionable according to us - return editor.editing; - } - }, - // CellEditing only activates individual cells. - activateRow: Ext.emptyFn, - deactivate: function() { - var me = this, - editors = me.editors.items, - len = editors.length, - i; - for (i = 0; i < len; i++) { - editors[i].hide(); - } - // This is a delayed method. - // Give the chance for the blur event to fire and bubble before - // ripping the editor from its place in the DOM and caching it in the - // detached body. - // - // We have to do this after deactivate in addition to hiding because we - // cannot tell when the grid's DOM is going to be destroyed and the editor's - // element may become "garbage" and be destroyed. - me.cacheDeactivatedEditors(); - }, - cacheDeactivatedEditors: function() { - var me = this, - editors = me.editors.items, - len = editors.length, - i, editor, - detachedBody = Ext.getDetachedBody(); - for (i = 0; i < len; i++) { - editor = editors[i]; - if (!editor.isVisible()) { - detachedBody.dom.appendChild(editor.el.dom); - editor.container = detachedBody; - } - } - }, - /** - * @deprecated 5.5.0 Use the grid's {@link Ext.panel.Table#setActionableMode actionable mode} to activate cell contents. - * Starts editing the specified record, using the specified Column definition to define which field is being edited. - * @param {Ext.data.Model/Number} record The Store data record which backs the row to be edited, or index of the record. - * @param {Ext.grid.column.Column/Number} columnHeader The Column object defining the column to be edited, or index of the column. - */ - startEdit: function(record, columnHeader) { - this.startEditByPosition(new Ext.grid.CellContext(this.view).setPosition(record, columnHeader)); - }, - completeEdit: function(remainVisible) { - var activeEd = this.getActiveEditor(); - if (activeEd) { - activeEd.completeEdit(remainVisible); - } - }, - // internal getters/setters - setEditingContext: function(context) { - this.context = context; - }, - setActiveEditor: function(ed) { - this.activeEditor = ed; - }, - getActiveEditor: function() { - return this.activeEditor; - }, - setActiveColumn: function(column) { - this.activeColumn = column; - }, - getActiveColumn: function() { - return this.activeColumn; - }, - setActiveRecord: function(record) { - this.activeRecord = record; - }, - getActiveRecord: function() { - return this.activeRecord; - }, - getEditor: function(record, column) { - var me = this, - editors = me.editors, - editorId = column.getItemId(), - editor = editors.getByKey(editorId); - if (!editor) { - editor = column.getEditor(record); - if (!editor) { - return false; - } - // Allow them to specify a CellEditor in the Column - if (editor instanceof Ext.grid.CellEditor) { - editor.floating = true; - } else // But if it's just a Field, wrap it. - { - editor = new Ext.grid.CellEditor({ - floating: true, - editorId: editorId, - field: editor - }); - } - // Add the Editor as a floating child of the grid - // Prevent this field from being included in an Ext.form.Basic - // collection, if the grid happens to be used inside a form - editor.field.excludeForm = true; - // If the editor is new to this grid, then add it to the grid, and ensure it tells us about its life cycle. - if (editor.column !== column) { - editor.column = column; - editor.on({ - scope: me, - complete: me.onEditComplete, - canceledit: me.cancelEdit - }); - column.on('removed', me.onColumnRemoved, me); - } - editors.add(editor); - } - // Inject an upward link to its owning grid even though it is not an added child. - editor.ownerCmp = me.grid.ownerGrid; - if (column.isTreeColumn) { - editor.isForTree = column.isTreeColumn; - editor.addCls(Ext.baseCSSPrefix + 'tree-cell-editor'); - } - // Set the owning grid. - // This needs to be kept up to date because in a Lockable assembly, an editor - // needs to swap sides if the column is moved across. - editor.setGrid(me.grid); - // Keep upward pointer correct for each use - editors are shared between locking sides - editor.editingPlugin = me; - return editor; - }, - onColumnRemoved: function(column) { - var me = this, - context = me.context; - // If the column was being edited, when plucked out of the grid, cancel the edit. - if (context && context.column === column) { - me.cancelEdit(); - } - // Remove the CellEditor of that column from the grid, and no longer listen for events from it. - column.un('removed', me.onColumnRemoved, me); - }, - setColumnField: function(column, field) { - var ed = this.editors.getByKey(column.getItemId()); - Ext.destroy(ed, column.field); - this.editors.removeAtKey(column.getItemId()); - this.callParent(arguments); - }, - /** - * Gets the cell (td) for a particular record and column. - * @param {Ext.data.Model} record - * @param {Ext.grid.column.Column} column - * @private - */ - getCell: function(record, column) { - return this.grid.getView().getCell(record, column); - }, - onEditComplete: function(ed, value, startValue) { - var me = this, - context = ed.context, - view, record; - view = context.view; - record = context.record; - context.value = value; - if (!me.validateEdit(context)) { - me.editing = false; - return; - } - // Only update the record if the new value is different than the - // startValue. When the view refreshes its el will gain focus - if (!record.isEqual(value, startValue)) { - record.set(context.column.dataIndex, value); - // Changing the record may impact the position - context.rowIdx = view.indexOf(record); - } - me.fireEvent('edit', me, context); - // We clear down our context here in response to the CellEditor completing. - // We only do this if we have not already started editing a new context. - if (me.context === context) { - me.setActiveEditor(null); - me.setActiveColumn(null); - me.setActiveRecord(null); - me.editing = false; - } - }, - /** - * Cancels any active editing. - */ - cancelEdit: function(activeEd) { - var me = this, - context = me.context; - // This is in response to the CellEditor firing a canceledit event. - if (activeEd && activeEd.isCellEditor) { - me.context.value = ('editedValue' in activeEd) ? activeEd.editedValue : activeEd.getValue(); - // Editing flag cleared in superclass. - // canceledit event fired in superclass. - me.callParent(arguments); - // Clear our current editing context. - // We only do this if we have not already started editing a new context. - if (activeEd.context === context) { - me.setActiveEditor(null); - me.setActiveColumn(null); - me.setActiveRecord(null); - } else // Re-instate editing flag after callParent - { - me.editing = true; - } - } else // This is a programmatic call to cancel any active edit - { - activeEd = me.getActiveEditor(); - if (activeEd && activeEd.field) { - activeEd.cancelEdit(); - } - } - }, - /** - * Starts editing by position (row/column) - * @param {Object} position A position with keys of row and column. - * Example usage: - * - * cellEditing.startEditByPosition({ - * row: 3, - * column: 2 - * }); - */ - startEditByPosition: function(position) { - var me = this, - cm = me.grid.getColumnManager(), - index, - activeEditor = me.getActiveEditor(); - // If a raw {row:0, column:0} object passed. - // The historic API is that column indices INCLUDE hidden columns, so use getColumnManager. - if (!position.isCellContext) { - position = new Ext.grid.CellContext(me.view).setPosition(position.row, me.grid.getColumnManager().getColumns()[position.column]); - } - // Coerce the edit column to the closest visible column. This typically - // only needs to be done when called programatically, since the position - // is handled by walkCells, which is called before this is invoked. - index = cm.getHeaderIndex(position.column); - position.column = cm.getVisibleHeaderClosestToIndex(index); - // Already in actionable mode. - if (me.grid.actionableMode) { - // We are being asked to edit right where we are (click in an active editor will get here) - if (me.editing && position.isEqual(me.context)) { - return; - } - // Finish any current edit. - if (activeEditor) { - activeEditor.completeEdit(); - } - } - // If we are STILL in actionable mode - synchronous blurring has not tipped us out of actionable mode... - if (me.grid.actionableMode) { - // Get the editor for the position, and if there is one, focus it - if (me.activateCell(position)) { - // Ensure the row is activated. - me.activateRow(me.view.all.item(position.rowIdx, true)); - activeEditor = me.getEditor(position.record, position.column); - if (activeEditor) { - activeEditor.field.focus(); - } - } - } else { - // Enter actionable mode at the requested position - return me.grid.setActionableMode(true, position); - } - } -}); - -/** - * This base class manages clipboard data transfer for a component. As an abstract class, - * applications use derived classes such as `{@link Ext.grid.plugin.Clipboard}` instead - * and seldom use this class directly. - * - * ## Operation - * - * Components that interact with the clipboard do so in two directions: copy and paste. - * When copying to the clipboard, a component will often provide multiple data formats. - * On paste, the consumer of the data can then decide what format it prefers and ignore - * the others. - * - * ### Copy (and Cut) - * - * There are two storage locations provided for holding copied data: - * - * * The system clipboard, used to exchange data with other applications running - * outside the browser. - * * A memory space in the browser page that can hold data for use only by other - * components on the page. This allows for richer formats to be transferred. - * - * A component can copy (or cut) data in multiple formats as controlled by the - * `{@link #cfg-memory}` and `{@link #cfg-system}` configs. - * - * ### Paste - * - * While there may be many formats available, when a component is ready to paste, only - * one format can ultimately be used. This is specified by the `{@link #cfg-source}` - * config. - * - * ## Browser Limitations - * - * At the current time, browsers have only a limited ability to interact with the system - * clipboard. The only reliable, cross-browser, plugin-in-free technique for doing so is - * to use invisible elements and focus tricks **during** the processing of clipboard key - * presses like CTRL+C (on Windows/Linux) or CMD+C (on Mac). - * - * @protected - * @since 5.1.0 - */ -Ext.define('Ext.plugin.AbstractClipboard', { - extend: 'Ext.plugin.Abstract', - requires: [ - 'Ext.util.KeyMap' - ], - cachedConfig: { - /** - * @cfg {Object} formats - * This object is keyed by the names of the data formats supported by this plugin. - * The property values of this object are objects with `get` and `put` properties - * that name the methods for getting data from (copy) and putting to into (paste) - * the associated component. - * - * For example: - * - * formats: { - * html: { - * get: 'getHtmlData', - * put: 'putHtmlData' - * } - * } - * - * This declares support for the "html" data format and indicates that the - * `getHtmlData` method should be called to copy HTML data from the component, - * while the `putHtmlData` should be called to paste HTML data into the component. - * - * By default, all derived classes must support a "text" format: - * - * formats: { - * text: { - * get: 'getTextData', - * put: 'putTextData' - * } - * } - * - * To understand the method signatures required to implement a data format, see the - * documentation for `{@link #getTextData}` and `{@link #putTextData}`. - * - * The format name "system" is not allowed. - * - * @protected - */ - formats: { - text: { - get: 'getTextData', - put: 'putTextData' - } - } - }, - config: { - /** - * @cfg {String/String[]} [memory] - * The data format(s) to copy to the private, memory clipboard. By default, data - * is not saved to the memory clipboard. Specify `true` to include all formats - * of data, or a string to copy a single format, or an array of strings to copy - * multiple formats. - */ - memory: null, - /** - * @cfg {String/String[]} [source="system"] - * The format or formats in order of preference when pasting data. This list can - * be any of the valid formats, plus the name "system". When a paste occurs, this - * config is consulted. The first format specified by this config that has data - * available in the private memory space is used. If "system" is encountered in - * the list, whatever data is available on the system clipboard is chosen. At - * that point, no further source formats will be considered. - */ - source: 'system', - /** - * @cfg {String} [system="text"] - * The data format to set in the system clipboard. By default, the "text" - * format is used. Based on the type of derived class, other formats may be - * possible. - */ - system: 'text' - }, - destroy: function() { - var me = this, - keyMap = me.keyMap, - shared = me.shared; - if (keyMap) { - // If we have a keyMap then we have incremented the shared usage counter - // and now need to remove ourselves. - me.keyMap = Ext.destroy(keyMap); - if (!--shared.counter) { - shared.textArea = Ext.destroy(shared.textArea); - } - } else { - // If we don't have a keyMap it is because we are waiting for the render - // event and haven't connected to the shared context. - me.renderListener = Ext.destroy(me.renderListener); - } - me.callParent(); - }, - init: function(comp) { - var me = this; - if (comp.rendered) { - this.finishInit(comp); - } else { - me.renderListener = comp.on({ - render: function() { - me.renderListener = null; - me.finishInit(comp); - }, - destroyable: true, - single: true - }); - } - }, - /** - * Returns the element target to listen to copy/paste. - * - * @param {Ext.Component} comp The component this plugin is initialized on. - * @return {Ext.dom.Element} The element target. - */ - getTarget: function(comp) { - return comp.el; - }, - /** - * This method returns the selected data in text format. - * @method getTextData - * @param {String} format The name of the format (i.e., "text"). - * @param {Boolean} erase Pass `true` to erase (cut) the data, `false` to just copy. - * @return {String} The data in text format. - */ - /** - * This method pastes the given text data. - * @method putTextData - * @param {Object} data The data in the indicated `format`. - * @param {String} format The name of the format (i.e., "text"). - */ - privates: { - /** - * @property {Object} shared - * The shared state for all clipboard-enabled components. - * @property {Number} shared.counter The number of clipboard-enabled components - * currently using this object. - * @property {Object} shared.data The clipboard data for intra-page copy/paste. The - * properties of the object are keyed by format. - * @property {Ext.dom.Element} textArea The shared textarea used to polyfill the - * lack of HTML5 clipboard API. - * @private - */ - shared: { - counter: 0, - data: null, - textArea: null - }, - applyMemory: function(value) { - // Same as "source" config but that allows "system" as a format. - value = this.applySource(value); - if (value) { - for (var i = value.length; i-- > 0; ) { - if (value[i] === 'system') { - Ext.raise('Invalid clipboard format "' + value[i] + '"'); - } - } - } - return value; - }, - applySource: function(value) { - // Make sure we have a non-empty String[] or null - if (value) { - if (Ext.isString(value)) { - value = [ - value - ]; - } else if (value.length === 0) { - value = null; - } - } - if (value) { - var formats = this.getFormats(); - for (var i = value.length; i-- > 0; ) { - if (value[i] !== 'system' && !formats[value[i]]) { - Ext.raise('Invalid clipboard format "' + value[i] + '"'); - } - } - } - return value || null; - }, - applySystem: function(value) { - var formats = this.getFormats(); - if (!formats[value]) { - Ext.raise('Invalid clipboard format "' + value + '"'); - } - return value; - }, - doCutCopy: function(event, erase) { - var me = this, - formats = me.allFormats || me.syncFormats(), - data = me.getData(erase, formats), - memory = me.getMemory(), - system = me.getSystem(), - sys; - me.shared.data = memory && data; - if (system) { - sys = data[system]; - if (formats[system] < 3) { - delete data[system]; - } - me.setClipboardData(sys); - } - }, - doPaste: function(format, data) { - var formats = this.getFormats(); - this[formats[format].put](data, format); - }, - finishInit: function(comp) { - var me = this; - me.keyMap = new Ext.util.KeyMap({ - target: me.getTarget(comp), - binding: [ - { - ctrl: true, - key: 'x', - fn: me.onCut, - scope: me - }, - { - ctrl: true, - key: 'c', - fn: me.onCopy, - scope: me - }, - { - ctrl: true, - key: 'v', - fn: me.onPaste, - scope: me - } - ] - }); - ++me.shared.counter; - comp.on({ - destroy: 'destroy', - scope: me - }); - }, - getData: function(erase, format) { - var me = this, - formats = me.getFormats(), - data, i, name, names; - if (Ext.isString(format)) { - if (!formats[format]) { - Ext.raise('Invalid clipboard format "' + format + '"'); - } - data = me[formats[format].get](format, erase); - } else { - data = {}; - names = []; - if (format) { - for (name in format) { - if (!formats[name]) { - Ext.raise('Invalid clipboard format "' + name + '"'); - } - names.push(name); - } - } else { - names = Ext.Object.getAllKeys(formats); - } - for (i = names.length; i-- > 0; ) { - data[name] = me[formats[name].get](name, erase && !i); - } - } - return data; - }, - /** - * @private - * @return {Ext.dom.Element} - */ - getHiddenTextArea: function() { - var shared = this.shared, - el; - el = shared.textArea; - if (!el) { - el = shared.textArea = Ext.getBody().createChild({ - tag: 'textarea', - tabIndex: -1, - // don't tab through this fellow - style: { - position: 'absolute', - top: '-1000px', - width: '1px', - height: '1px' - } - }); - // We don't want this element to fire focus events ever - el.suspendFocusEvents(); - } - return el; - }, - onCopy: function(keyCode, event) { - this.doCutCopy(event, false); - }, - onCut: function(keyCode, event) { - this.doCutCopy(event, true); - }, - onPaste: function(keyCode, event) { - var me = this, - sharedData = me.shared.data, - source = me.getSource(), - i, n, s; - if (source) { - for (i = 0 , n = source.length; i < n; ++i) { - s = source[i]; - if (s === 'system') { - // get the format used by the system clipboard. - s = me.getSystem(); - me.pasteClipboardData(s); - break; - } else if (sharedData && (s in sharedData)) { - me.doPaste(s, sharedData[s]); - break; - } - } - } - }, - pasteClipboardData: function(format) { - var me = this, - clippy = window.clipboardData, - area, focusEl; - if (clippy && clippy.getData) { - me.doPaste(format, clippy.getData("text")); - } else { - focusEl = Ext.Element.getActiveElement(true); - area = me.getHiddenTextArea().dom; - area.value = ''; - // We must not disturb application state by doing this focus - if (focusEl) { - focusEl.suspendFocusEvents(); - } - area.focus(); - // Between now and the deferred function, the CTRL+V hotkey will have - // its default action processed which will paste the clipboard content - // into the textarea. - Ext.defer(function() { - // Focus back to the real destination - if (focusEl) { - focusEl.focus(); - // Restore framework focus handling - focusEl.resumeFocusEvents(); - } - me.doPaste(format, area.value); - area.value = ''; - }, 100, me); - } - }, - setClipboardData: function(data) { - var clippy = window.clipboardData; - if (clippy && clippy.setData) { - clippy.setData("text", data); - } else { - var me = this, - area = me.getHiddenTextArea().dom, - focusEl = Ext.Element.getActiveElement(true); - area.value = data; - // We must not disturb application state by doing this focus - if (focusEl) { - focusEl.suspendFocusEvents(); - } - area.focus(); - area.select(); - // Between now and the deferred function, the CTRL+C/X hotkey will have - // its default action processed which will update the clipboard from the - // textarea. - Ext.defer(function() { - area.value = ''; - if (focusEl) { - focusEl.focus(); - // Restore framework focus handling - focusEl.resumeFocusEvents(); - } - }, 50); - } - }, - syncFormats: function() { - var me = this, - map = {}, - memory = me.getMemory(), - system = me.getSystem(), - i, s; - if (system) { - map[system] = 1; - } - if (memory) { - for (i = memory.length; i-- > 0; ) { - s = memory[i]; - map[s] = map[s] ? 3 : 2; - } - } - // 1: memory - // 2: system - // 3: both - return me.allFormats = map; - }, - // jshint ignore:line - updateMemory: function() { - this.allFormats = null; - }, - updateSystem: function() { - this.allFormats = null; - } - } -}); - -/** - * This {@link Ext.grid.Panel grid} plugin adds clipboard support to a grid. - * - * *Note that the grid must use the {@link Ext.grid.selection.SpreadsheetModel spreadsheet selection model} to utilize this plugin.* - * - * This class supports the following `{@link Ext.plugin.AbstractClipboard#formats formats}` - * for grid data: - * - * * `cell` - Complete field data that can be matched to other grids using the same - * {@link Ext.data.Model model} regardless of column order. - * * `text` - Cell content stripped of HTML tags. - * * `html` - Complete cell content, including any rendered HTML tags. - * * `raw` - Underlying field values based on `dataIndex`. - * - * The `cell` format is not valid for the `{@link Ext.plugin.AbstractClipboard#system system}` - * clipboard format. - */ -Ext.define('Ext.grid.plugin.Clipboard', { - extend: 'Ext.plugin.AbstractClipboard', - alias: 'plugin.clipboard', - requires: [ - 'Ext.util.Format', - 'Ext.util.TSV' - ], - formats: { - cell: { - get: 'getCells' - }, - html: { - get: 'getCellData' - }, - raw: { - get: 'getCellData' - } - }, - getCellData: function(format, erase) { - var cmp = this.getCmp(), - selModel = cmp.getSelectionModel(), - ret = [], - isRaw = format === 'raw', - isText = format === 'text', - viewNode, cell, data, dataIndex, lastRecord, column, record, row, view; - selModel.getSelected().eachCell(function(cellContext) { - column = cellContext.column , view = cellContext.column.getView(); - record = cellContext.record; - // Do not copy the check column or row numberer column - if (column.ignoreExport) { - return; - } - if (lastRecord !== record) { - lastRecord = record; - ret.push(row = []); - } - dataIndex = column.dataIndex; - if (isRaw) { - data = record.data[dataIndex]; - } else { - // Try to access the view node. - viewNode = view.all.item(cellContext.rowIdx); - // If we could not, it's because it's outside of the rendered block - recreate it. - if (!viewNode) { - viewNode = Ext.fly(view.createRowElement(record, cellContext.rowIdx)); - } - cell = viewNode.down(column.getCellInnerSelector()); - data = cell.dom.innerHTML; - if (isText) { - data = Ext.util.Format.stripTags(data); - } - } - row.push(data); - if (erase && dataIndex) { - record.set(dataIndex, null); - } - }); - return Ext.util.TSV.encode(ret); - }, - getCells: function(format, erase) { - var cmp = this.getCmp(), - selModel = cmp.getSelectionModel(), - ret = [], - dataIndex, lastRecord, record, row; - selModel.getSelected().eachCell(function(cellContext) { - record = cellContext.record; - if (lastRecord !== record) { - lastRecord = record; - ret.push(row = { - model: record.self, - fields: [] - }); - } - dataIndex = cellContext.column.dataIndex; - row.fields.push({ - name: dataIndex, - value: record.data[dataIndex] - }); - if (erase && dataIndex) { - record.set(dataIndex, null); - } - }); - return ret; - }, - getTextData: function(format, erase) { - return this.getCellData(format, erase); - }, - putCellData: function(data, format) { - var values = Ext.util.TSV.decode(data), - row, - recCount = values.length, - colCount = recCount ? values[0].length : 0, - sourceRowIdx, sourceColIdx, - view = this.getCmp().getView(), - maxRowIdx = view.dataSource.getCount() - 1, - maxColIdx = view.getVisibleColumnManager().getColumns().length - 1, - navModel = view.getNavigationModel(), - destination = navModel.getPosition(), - dataIndex, destinationStartColumn, - dataObject = {}; - // If the view is not focused, use the first cell of the selection as the destination. - if (!destination) { - view.getSelectionModel().getSelected().eachCell(function(c) { - destination = c; - return false; - }); - } - if (destination) { - // Create a new Context based upon the outermost View. - // NavigationModel works on local views. TODO: remove this step when NavModel is fixed to use outermost view in locked grid. - // At that point, we can use navModel.getPosition() - destination = new Ext.grid.CellContext(view).setPosition(destination.record, destination.column); - } else { - destination = new Ext.grid.CellContext(view).setPosition(0, 0); - } - destinationStartColumn = destination.colIdx; - for (sourceRowIdx = 0; sourceRowIdx < recCount; sourceRowIdx++) { - row = values[sourceRowIdx]; - // Collect new values in dataObject - for (sourceColIdx = 0; sourceColIdx < colCount; sourceColIdx++) { - dataIndex = destination.column.dataIndex; - if (dataIndex) { - switch (format) { - // Raw field values - case 'raw': - dataObject[dataIndex] = row[sourceColIdx]; - break; - // Textual data with HTML tags stripped - case 'text': - dataObject[dataIndex] = row[sourceColIdx]; - break; - // innerHTML from the cell inner - case 'html': - break; - } - } - // If we are at the end of the destination row, break the column loop. - if (destination.colIdx === maxColIdx) { - break; - } - destination.setColumn(destination.colIdx + 1); - } - // Update the record in one go. - destination.record.set(dataObject); - // If we are at the end of the destination store, break the row loop. - if (destination.rowIdx === maxRowIdx) { - break; - } - // Jump to next row in destination - destination.setPosition(destination.rowIdx + 1, destinationStartColumn); - } - }, - putTextData: function(data, format) { - this.putCellData(data, format); - }, - getTarget: function(comp) { - return comp.body; - } -}); - -/** - * This plugin provides drag and drop functionality for a {@link Ext.grid.View GridView}. - * - * A specialized instance of {@link Ext.dd.DragZone DragZone} and {@link Ext.dd.DropZone - * DropZone} are attached to the grid view. The DropZone will participate in drops - * from DragZones having the same {@link #ddGroup} including drops from within the same - * grid. - * - * During the drop operation a data object is passed to a participating DropZone's drop - * handlers. The drag data object has the following properties: - * - * - **copy:** {@link Boolean}
    The value of {@link #copy}. Or `true` if - * {@link #allowCopy} is true **and** the control key was pressed as the drag operation - * began. - * - * - **view:** {@link Ext.grid.View GridView}
    The source grid view from which the - * drag originated - * - * - **ddel:** HTMLElement
    The drag proxy element which moves with the cursor - * - * - **item:** HTMLElement
    The grid view node upon which the mousedown event was - * registered - * - * - **records:** {@link Array}
    An Array of {@link Ext.data.Model Model}s - * representing the selected data being dragged from the source grid view - * - * By adding this plugin to a view, two new events will be fired from the client - * grid view as well as its owning Grid: `{@link #beforedrop}` and `{@link #drop}`. - * - * @example - * var store = Ext.create('Ext.data.Store', { - * fields: ['name'], - * data: [ - * ["Lisa"], - * ["Bart"], - * ["Homer"], - * ["Marge"] - * ], - * proxy: { - * type: 'memory', - * reader: 'array' - * } - * }); - * - * Ext.create('Ext.grid.Panel', { - * store: store, - * enableLocking: true, - * columns: [{ - * header: 'Name', - * dataIndex: 'name', - * flex: true - * }], - * viewConfig: { - * plugins: { - * ptype: 'gridviewdragdrop', - * dragText: 'Drag and drop to reorganize' - * } - * }, - * height: 200, - * width: 400, - * renderTo: Ext.getBody() - * }); - */ -Ext.define('Ext.grid.plugin.DragDrop', { - extend: 'Ext.plugin.Abstract', - alias: 'plugin.gridviewdragdrop', - uses: [ - 'Ext.view.DragZone', - 'Ext.grid.ViewDropZone' - ], - /** - * @event beforedrop - * **This event is fired through the {@link Ext.grid.View GridView} and its owning - * {@link Ext.grid.Panel Grid}. You can add listeners to the grid or grid {@link - * Ext.grid.Panel#viewConfig view config} object** - * - * Fired when a drop gesture has been triggered by a mouseup event in a valid drop - * position in the grid view. - * - * Returning `false` to this event signals that the drop gesture was invalid and - * animates the drag proxy back to the point from which the drag began. - * - * The dropHandlers parameter can be used to defer the processing of this event. For - * example, you can force the handler to wait for the result of a message box - * confirmation or an asynchronous server call (_see the details of the dropHandlers - * property for more information_). - * - * grid.on('beforedrop', function(node, data, overModel, dropPosition, dropHandlers) { - * // Defer the handling - * dropHandlers.wait = true; - * Ext.MessageBox.confirm('Drop', 'Are you sure', function(btn){ - * if (btn === 'yes') { - * dropHandlers.processDrop(); - * } else { - * dropHandlers.cancelDrop(); - * } - * }); - * }); - * - * Any other return value continues with the data transfer operation unless the wait - * property is set. - * - * @param {HTMLElement} node The {@link Ext.grid.View grid view} node **if any** over - * which the cursor was positioned. - * - * @param {Object} data The data object gathered at mousedown time by the - * cooperating {@link Ext.dd.DragZone DragZone}'s {@link Ext.dd.DragZone#getDragData - * getDragData} method. It contains the following properties: - * @param {Boolean} data.copy The value of {@link #copy}. Or `true` if - * {@link #allowCopy} is true **and** the control key was pressed as the drag - * operation began. - * @param {Ext.grid.View} data.view The source grid view from which the drag - * originated - * @param {HTMLElement} data.ddel The drag proxy element which moves with the cursor - * @param {HTMLElement} data.item The grid view node upon which the mousedown event - * was registered - * @param {Ext.data.Model[]} data.records An Array of Models representing the - * selected data being dragged from the source grid view - * - * @param {Ext.data.Model} overModel The Model over which the drop gesture took place - * - * @param {String} dropPosition `"before"` or `"after"` depending on whether the - * cursor is above or below the mid-line of the node. - * - * @param {Object} dropHandlers - * This parameter allows the developer to control when the drop action takes place. - * It is useful if any asynchronous processing needs to be completed before - * performing the drop. This object has the following properties: - * - * @param {Boolean} dropHandlers.wait Indicates whether the drop should be deferred. - * Set this property to true to defer the drop. - * @param {Function} dropHandlers.processDrop A function to be called to complete - * the drop operation. - * @param {Function} dropHandlers.cancelDrop A function to be called to cancel the - * drop operation. - */ - /** - * @event drop - * **This event is fired through the {@link Ext.grid.View GridView} and its owning - * {@link Ext.grid.Panel Grid}. You can add listeners to the grid or grid {@link - * Ext.grid.Panel#viewConfig view config} object** - * - * Fired when a drop operation has been completed and the data has been moved or - * copied. - * - * @param {HTMLElement} node The {@link Ext.grid.View GridView} node **if any** over - * which the cursor was positioned. - * - * @param {Object} data The data object gathered at mousedown time by the - * cooperating {@link Ext.dd.DragZone DragZone}'s {@link Ext.dd.DragZone#getDragData - * getDragData} method. It contains the following properties: - * @param {Boolean} data.copy The value of {@link #copy}. Or `true` if - * {@link #allowCopy} is true **and** the control key was pressed as the drag - * operation began. - * @param {Ext.grid.View} data.view The source grid view from which the drag - * originated - * @param {HTMLElement} data.ddel The drag proxy element which moves with the cursor - * @param {HTMLElement} data.item The grid view node upon which the mousedown event - * was registered - * @param {Ext.data.Model[]} data.records An Array of Models representing the - * selected data being dragged from the source grid view - * - * @param {Ext.data.Model} overModel The Model over which the drop gesture took - * place. - * - * @param {String} dropPosition `"before"` or `"after"` depending on whether the - * cursor is above or below the mid-line of the node. - */ - /** - * @cfg {Boolean} [copy=false] - * Set as `true` to copy the records from the source grid to the destination drop - * grid. Otherwise, dragged records will be moved. - * - * **Note:** This only applies to records dragged between two different grids with - * unique stores. - * - * See {@link #allowCopy} to allow only control-drag operations to copy records. - */ - /** - * @cfg {Boolean} [allowCopy=false] - * Set as `true` to allow the user to hold down the control key at the start of the - * drag operation and copy the dragged records between grids. Otherwise, dragged - * records will be moved. - * - * **Note:** This only applies to records dragged between two different grids with - * unique stores. - * - * See {@link #copy} to enable the copying of all dragged records. - */ - // - /** - * @cfg - * The text to show while dragging. - * - * Two placeholders can be used in the text: - * - * - `{0}` The number of selected items. - * - `{1}` 's' when more than 1 items (only useful for English). - */ - dragText: '{0} selected row{1}', - // - /** - * @cfg {String} [ddGroup=gridDD] - * A named drag drop group to which this object belongs. If a group is specified, then both the DragZones and - * DropZone used by this plugin will only interact with other drag drop objects in the same group. - */ - ddGroup: "GridDD", - /** - * @cfg {String} [dragGroup] - * The {@link #ddGroup} to which the DragZone will belong. - * - * This defines which other DropZones the DragZone will interact with. Drag/DropZones only interact with other - * Drag/DropZones which are members of the same {@link #ddGroup}. - */ - /** - * @cfg {String} [dropGroup] - * The {@link #ddGroup} to which the DropZone will belong. - * - * This defines which other DragZones the DropZone will interact with. Drag/DropZones only interact with other - * Drag/DropZones which are members of the same {@link #ddGroup}. - */ - /** - * @cfg {Boolean} enableDrop - * `false` to disallow the View from accepting drop gestures. - */ - enableDrop: true, - /** - * @cfg {Boolean} enableDrag - * `false` to disallow dragging items from the View. - */ - enableDrag: true, - /** - * `true` to register this container with the Scrollmanager for auto scrolling during drag operations. - * A {@link Ext.dd.ScrollManager} configuration may also be passed. - * @cfg {Object/Boolean} containerScroll - */ - containerScroll: false, - /** - * @cfg {Object} [dragZone] - * A config object to apply to the creation of the {@link #property-dragZone DragZone} which handles for drag start gestures. - * - * Template methods of the DragZone may be overridden using this config. - */ - /** - * @cfg {Object} [dropZone] - * A config object to apply to the creation of the {@link #property-dropZone DropZone} which handles mouseover and drop gestures. - * - * Template methods of the DropZone may be overridden using this config. - */ - /** - * @property {Ext.view.DragZone} dragZone - * An {@link Ext.view.DragZone DragZone} which handles mousedown and dragging of records from the grid. - */ - /** - * @property {Ext.grid.ViewDropZone} dropZone - * An {@link Ext.grid.ViewDropZone DropZone} which handles mouseover and dropping records in any grid which shares the same {@link #dropGroup}. - */ - init: function(view) { - Ext.applyIf(view, { - copy: this.copy, - allowCopy: this.allowCopy - }); - view.on('render', this.onViewRender, this, { - single: true - }); - }, - /** - * @private - * Component calls destroy on all its plugins at destroy time. - */ - destroy: function() { - var me = this; - me.dragZone = me.dropZone = Ext.destroy(me.dragZone, me.dropZone); - me.callParent(); - }, - enable: function() { - var me = this; - if (me.dragZone) { - me.dragZone.unlock(); - } - if (me.dropZone) { - me.dropZone.unlock(); - } - me.callParent(); - }, - disable: function() { - var me = this; - if (me.dragZone) { - me.dragZone.lock(); - } - if (me.dropZone) { - me.dropZone.lock(); - } - me.callParent(); - }, - onViewRender: function(view) { - var me = this, - ownerGrid = view.ownerCt.ownerGrid || view.ownerCt, - scrollEl; - ownerGrid.relayEvents(view, [ - 'beforedrop', - 'drop' - ]); - if (me.enableDrag) { - if (me.containerScroll) { - scrollEl = view.getEl(); - } - me.dragZone = new Ext.view.DragZone(Ext.apply({ - view: view, - ddGroup: me.dragGroup || me.ddGroup, - dragText: me.dragText, - containerScroll: me.containerScroll, - scrollEl: scrollEl - }, me.dragZone)); - } - if (me.enableDrop) { - me.dropZone = new Ext.grid.ViewDropZone(Ext.apply({ - view: view, - ddGroup: me.dropGroup || me.ddGroup - }, me.dropZone)); - } - } -}); - -/** - * The Ext.grid.plugin.RowEditing plugin injects editing at a row level for a Grid. When editing begins, - * a small floating dialog will be shown for the appropriate row. Each editable column will show a field - * for editing. There is a button to save or cancel all changes for the edit. - * - * The field that will be used for the editor is defined at the - * {@link Ext.grid.column.Column#editor editor}. The editor can be a field instance or a field configuration. - * If an editor is not specified for a particular column then that column won't be editable and the value of - * the column will be displayed. To provide a custom renderer for non-editable values, use the - * {@link Ext.grid.column.Column#editRenderer editRenderer} configuration on the column. - * - * The editor may be shared for each column in the grid, or a different one may be specified for each column. - * An appropriate field type should be chosen to match the data structure that it will be editing. For example, - * to edit a date, it would be useful to specify {@link Ext.form.field.Date} as the editor. - * - * @example - * Ext.create('Ext.data.Store', { - * storeId: 'simpsonsStore', - * fields:[ 'name', 'email', 'phone'], - * data: [ - * { name: 'Lisa', email: 'lisa@simpsons.com', phone: '555-111-1224' }, - * { name: 'Bart', email: 'bart@simpsons.com', phone: '555-222-1234' }, - * { name: 'Homer', email: 'homer@simpsons.com', phone: '555-222-1244' }, - * { name: 'Marge', email: 'marge@simpsons.com', phone: '555-222-1254' } - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Simpsons', - * store: Ext.data.StoreManager.lookup('simpsonsStore'), - * columns: [ - * {header: 'Name', dataIndex: 'name', editor: 'textfield'}, - * {header: 'Email', dataIndex: 'email', flex:1, - * editor: { - * xtype: 'textfield', - * allowBlank: false - * } - * }, - * {header: 'Phone', dataIndex: 'phone'} - * ], - * selModel: 'rowmodel', - * plugins: { - * ptype: 'rowediting', - * clicksToEdit: 1 - * }, - * height: 200, - * width: 400, - * renderTo: Ext.getBody() - * }); - * - */ -Ext.define('Ext.grid.plugin.RowEditing', { - extend: 'Ext.grid.plugin.Editing', - alias: 'plugin.rowediting', - requires: [ - 'Ext.grid.RowEditor' - ], - lockableScope: 'top', - editStyle: 'row', - /** - * @cfg {Boolean} autoCancel - * `true` to automatically cancel any pending changes when the row editor begins editing a new row. - * `false` to force the user to explicitly cancel the pending changes. - */ - autoCancel: true, - /** - * @cfg {Number} clicksToMoveEditor - * The number of clicks to move the row editor to a new row while it is visible and actively editing another row. - * This will default to the same value as {@link Ext.grid.plugin.Editing#clicksToEdit clicksToEdit}. - */ - /** - * @cfg {Boolean} errorSummary - * True to show a {@link Ext.tip.ToolTip tooltip} that summarizes all validation errors present - * in the row editor. Set to false to prevent the tooltip from showing. - */ - errorSummary: true, - constructor: function() { - var me = this; - me.callParent(arguments); - if (!me.clicksToMoveEditor) { - me.clicksToMoveEditor = me.clicksToEdit; - } - me.autoCancel = !!me.autoCancel; - }, - init: function(grid) { - this.callParent([ - grid - ]); - // This plugin has an interest in processing a request for actionable mode. - // It does not actually enter actionable mode, it just calls startEdit - if (grid.lockedGrid) { - grid.lockedGrid.registerActionable(this); - grid.normalGrid.registerActionable(this); - } else { - grid.registerActionable(this); - } - }, - destroy: function() { - Ext.destroy(this.editor); - this.callParent(); - }, - onBeforeReconfigure: function() { - this.callParent(arguments); - this.cancelEdit(); - }, - onReconfigure: function(grid, store, columns) { - var ed = this.editor; - this.callParent(arguments); - // Only need to adjust column widths if we have new columns - if (columns && ed && ed.rendered) { - ed.needsSyncFieldWidths = true; - } - }, - shouldStartEdit: function(editor) { - return true; - }, - /** - * Starts editing the specified record, using the specified Column definition to define which field is being edited. - * @param {Ext.data.Model} record The Store data record which backs the row to be edited. - * @param {Ext.grid.column.Column/Number} [columnHeader] The Column object defining the column field to be focused, or index of the column. - * If not specified, it will default to the first visible column. - * @return {Boolean} `true` if editing was started, `false` otherwise. - */ - startEdit: function(record, columnHeader) { - var me = this, - editor = me.getEditor(), - context; - if (Ext.isEmpty(columnHeader)) { - columnHeader = me.grid.getTopLevelVisibleColumnManager().getHeaderAtIndex(0); - } - if (editor.beforeEdit() !== false) { - context = me.getEditingContext(record, columnHeader); - if (context && me.beforeEdit(context) !== false && me.fireEvent('beforeedit', me, context) !== false && !context.cancel) { - me.context = context; - // If editing one side of a lockable grid, cancel any edit on the other side. - if (me.lockingPartner) { - me.lockingPartner.cancelEdit(); - } - editor.startEdit(context.record, context.column, context); - me.editing = true; - return true; - } - } - return false; - }, - /** - * This method is called when actionable mode is requested for a cell. - * @param {Ext.grid.CellContext} position The position at which actionable mode was requested. - * @return {Boolean} `false` Actionable mode is *not* entered for RowEditing. - * @protected - */ - activateCell: function(pos) { - // Only activate editing if there are no readily activatable elements in the activate position. - // We defer to those focusables. Editing may be started on other columns. - if (!pos.getCell().query('[tabIndex="-1"]').length) { - this.startEdit(pos.record, pos.column); - return true; - } - }, - /** - * @private - * The {@link Ext.grid.RowEditor RowEditor} hooks up a KeyNav to call this method to complete the edit. - */ - onEnterKey: function(e) { - var me = this, - targetComponent; - // KeyMap entry for EnterKey added after the entry that sets actionable mode, so this will get called - // after that handler. We must ignore ENTER key in actionable mode. - if (!me.grid.ownerGrid.actionableMode && me.editing) { - targetComponent = Ext.getCmp(e.getTarget().getAttribute('componentId')); - // ENTER when a picker is expanded does not complete the edit - if (!(targetComponent && targetComponent.isPickerField && targetComponent.isExpanded)) { - me.completeEdit(); - } - } - }, - cancelEdit: function() { - var me = this; - if (me.editing) { - me.getContextFieldValues(); - me.getEditor().cancelEdit(); - me.callParent(arguments); - return; - } - // If we aren't editing, return true to allow the event to bubble - return true; - }, - completeEdit: function() { - var me = this, - context = me.context; - if (me.editing && me.validateEdit(context)) { - me.editing = false; - me.fireEvent('edit', me, context); - } - }, - validateEdit: function() { - this.getContextFieldValues(); - return this.callParent(arguments) && this.getEditor().completeEdit(); - }, - getEditor: function() { - var me = this; - if (!me.editor) { - me.editor = me.initEditor(); - } - return me.editor; - }, - getContextFieldValues: function() { - var editor = this.editor, - context = this.context, - record = context.record, - newValues = {}, - originalValues = {}, - editors = editor.query('>[isFormField]'), - len = editors.length, - i, name, item; - for (i = 0; i < len; i++) { - item = editors[i]; - name = item.dataIndex; - newValues[name] = item.getValue(); - originalValues[name] = record.get(name); - } - Ext.apply(context, { - newValues: newValues, - originalValues: originalValues - }); - }, - /** - * @private - */ - initEditor: function() { - return new Ext.grid.RowEditor(this.initEditorConfig()); - }, - initEditorConfig: function() { - var me = this, - grid = me.grid, - view = me.view, - headerCt = grid.headerCt, - btns = [ - 'saveBtnText', - 'cancelBtnText', - 'errorsText', - 'dirtyText' - ], - b, - bLen = btns.length, - cfg = { - autoCancel: me.autoCancel, - errorSummary: me.errorSummary, - fields: headerCt.getGridColumns(), - hidden: true, - view: view, - // keep a reference.. - editingPlugin: me - }, - item; - for (b = 0; b < bLen; b++) { - item = btns[b]; - if (Ext.isDefined(me[item])) { - cfg[item] = me[item]; - } - } - return cfg; - }, - /** - * @private - */ - initEditTriggers: function() { - var me = this, - view = me.view, - moveEditorEvent = me.clicksToMoveEditor === 1 ? 'click' : 'dblclick'; - me.callParent(arguments); - if (me.clicksToMoveEditor !== me.clicksToEdit) { - me.mon(view, 'cell' + moveEditorEvent, me.moveEditorByClick, me); - } - view.on({ - render: function() { - me.mon(me.grid.headerCt, { - scope: me, - columnresize: me.onColumnResize, - columnhide: me.onColumnHide, - columnshow: me.onColumnShow - }); - }, - single: true - }); - }, - moveEditorByClick: function() { - var me = this; - if (me.editing) { - me.superclass.onCellClick.apply(me, arguments); - } - }, - /** - * @private - */ - onColumnAdd: function(ct, column) { - if (column.isHeader) { - var me = this, - editor; - me.initFieldAccessors(column); - // Only inform the editor about a new column if the editor has already been instantiated, - // so do not use getEditor which instantiates the editor if not present. - editor = me.editor; - if (editor) { - editor.onColumnAdd(column); - } - } - }, - // Ensure editors are cleaned up. - beforeGridHeaderDestroy: function(headerCt) { - var columns = this.grid.getColumnManager().getColumns(), - len = columns.length, - i, column, field; - for (i = 0; i < len; i++) { - column = columns[i]; - // If it has a field accessor, then destroy any field, and remove the accessors. - if (column.hasEditor) { - if (column.hasEditor() && (field = column.getEditor())) { - field.destroy(); - } - this.removeFieldAccessors(column); - } - } - }, - /** - * @private - */ - onColumnResize: function(ct, column, width) { - if (column.isHeader) { - var me = this, - editor = me.getEditor(); - if (editor) { - editor.onColumnResize(column, width); - } - } - }, - /** - * @private - */ - onColumnHide: function(ct, column) { - // no isHeader check here since its already a columnhide event. - var me = this, - editor = me.getEditor(); - if (editor) { - editor.onColumnHide(column); - } - }, - /** - * @private - */ - onColumnShow: function(ct, column) { - // no isHeader check here since its already a columnshow event. - var me = this, - editor = me.getEditor(); - if (editor) { - editor.onColumnShow(column); - } - }, - /** - * @private - */ - onColumnMove: function(ct, column, fromIdx, toIdx) { - // no isHeader check here since its already a columnmove event. - var me = this, - editor = me.getEditor(); - // Inject field accessors on move because if the move FROM the main headerCt and INTO a grouped header, - // the accessors will have been deleted but not added. They are added conditionally. - me.initFieldAccessors(column); - if (editor) { - // Must adjust the toIdx to account for removal if moving rightwards - // because RowEditor.onColumnMove just calls Container.move which does not do this. - editor.onColumnMove(column, fromIdx, toIdx); - } - }, - /** - * @private - */ - setColumnField: function(column, field) { - var me = this, - editor = me.getEditor(); - if (editor) { - // Remove the old editor and destroy it. - editor.destroyColumnEditor(column); - } - me.callParent(arguments); - if (editor) { - editor.insertColumnEditor(column); - } - }, - createColumnField: function(column, defaultField) { - var editor = this.editor, - def; - if (editor) { - def = editor.getDefaultFieldCfg(); - } - return this.callParent([ - column, - defaultField || def - ]); - } -}); - -// feature idea to enable Ajax loading and then the content -// cache would actually make sense. Should we dictate that they use -// data or support raw html as well? -/** - * Plugin (ptype = 'rowexpander') that adds the ability to have a Column in a grid which enables - * a second row body which expands/contracts. The expand/contract behavior is configurable to react - * on clicking of the column, double click of the row, and/or hitting enter while a row is selected. - */ -Ext.define('Ext.grid.plugin.RowExpander', { - extend: 'Ext.plugin.Abstract', - lockableScope: 'normal', - requires: [ - 'Ext.grid.feature.RowBody' - ], - alias: 'plugin.rowexpander', - /** - * @cfg {Number} [columnWidth=24] - * The width of the row expander column which contains the [+]/[-] icons to toggle row expansion. - */ - columnWidth: 24, - /** - * @cfg {Ext.XTemplate} rowBodyTpl - * An XTemplate which, when passed a record data object, produces HTML for the expanded row content. - * - * Note that if this plugin is applied to a lockable grid, the rowBodyTpl applies to the normal (unlocked) side. - * See {@link #lockedTpl} - * - */ - rowBodyTpl: null, - /** - * @cfg {Ext.XTemplate} [lockedTpl] - * An XTemplate which, when passed a record data object, produces HTML for the expanded row content *on the locked side of a lockable grid*. - */ - lockedTpl: null, - /** - * @cfg {Boolean} expandOnEnter - * This config is no longer supported. The Enter key initiated the grid's actinoable mode. - */ - /** - * @cfg {Boolean} expandOnDblClick - * `true` to toggle a row between expanded/collapsed when double clicked - * (defaults to `true`). - */ - expandOnDblClick: true, - /** - * @cfg {Boolean} selectRowOnExpand - * `true` to select a row when clicking on the expander icon - * (defaults to `false`). - */ - selectRowOnExpand: false, - /** - * @cfg {Number} - * The width of the Row Expander column header - */ - headerWidth: 24, - /** - * @cfg {Boolean} [bodyBefore=false] - * Configure as `true` to put the row expander body *before* the data row. - * - */ - bodyBefore: false, - rowBodyTrSelector: '.' + Ext.baseCSSPrefix + 'grid-rowbody-tr', - rowBodyHiddenCls: Ext.baseCSSPrefix + 'grid-row-body-hidden', - rowCollapsedCls: Ext.baseCSSPrefix + 'grid-row-collapsed', - addCollapsedCls: { - fn: function(out, values, parent) { - var me = this.rowExpander; - if (!me.recordsExpanded[values.record.internalId]) { - values.itemClasses.push(me.rowCollapsedCls); - } - this.nextTpl.applyOut(values, out, parent); - }, - syncRowHeights: function(lockedItem, normalItem) { - this.rowExpander.syncRowHeights(lockedItem, normalItem); - }, - // We need a high priority to get in ahead of the outerRowTpl - // so we can setup row data - priority: 20000 - }, - /** - * @event expandbody - * **Fired through the grid's View** - * @param {HTMLElement} rowNode The <tr> element which owns the expanded row. - * @param {Ext.data.Model} record The record providing the data. - * @param {HTMLElement} expandRow The <tr> element containing the expanded data. - */ - /** - * @event collapsebody - * **Fired through the grid's View.** - * @param {HTMLElement} rowNode The <tr> element which owns the expanded row. - * @param {Ext.data.Model} record The record providing the data. - * @param {HTMLElement} expandRow The <tr> element containing the expanded data. - */ - setCmp: function(grid) { - var me = this, - features; - me.callParent(arguments); - me.recordsExpanded = {}; - if (!me.rowBodyTpl) { - Ext.raise("The 'rowBodyTpl' config is required and is not defined."); - } - me.rowBodyTpl = Ext.XTemplate.getTpl(me, 'rowBodyTpl'); - features = me.getFeatureConfig(grid); - if (grid.features) { - grid.features = Ext.Array.push(features, grid.features); - } else { - grid.features = features; - } - }, - // NOTE: features have to be added before init (before Table.initComponent) - /** - * @protected - * @return {Array} And array of Features or Feature config objects. - * Returns the array of Feature configurations needed to make the RowExpander work. - * May be overridden in a subclass to modify the returned array. - */ - getFeatureConfig: function(grid) { - var me = this, - features = [], - featuresCfg = { - ftype: 'rowbody', - rowExpander: me, - bodyBefore: me.bodyBefore, - recordsExpanded: me.recordsExpanded, - rowBodyHiddenCls: me.rowBodyHiddenCls, - rowCollapsedCls: me.rowCollapsedCls, - setupRowData: me.getRowBodyFeatureData, - setup: me.setup - }; - features.push(Ext.apply({ - lockableScope: 'normal', - getRowBodyContents: me.getRowBodyContentsFn(me.rowBodyTpl) - }, featuresCfg)); - // Locked side will need a copy to keep the two DOM structures symmetrical. - // A lockedTpl config is available to create content in locked side. - // The enableLocking flag is set early in Ext.panel.Table#initComponent if any columns are locked. - if (grid.enableLocking) { - features.push(Ext.apply({ - lockableScope: 'locked', - getRowBodyContents: me.lockedTpl ? me.getRowBodyContentsFn(me.lockedTpl) : function() { - return ''; - } - }, featuresCfg)); - } - return features; - }, - getRowBodyContentsFn: function(rowBodyTpl) { - var me = this; - return function(rowValues) { - rowBodyTpl.owner = me; - return rowBodyTpl.applyTemplate(rowValues.record.getData()); - }; - }, - init: function(grid) { - if (grid.lockable) { - grid = grid.normalGrid; - } - var me = this, - ownerLockable = grid.ownerLockable, - view, lockedView; - me.callParent(arguments); - me.grid = grid; - view = me.view = grid.getView(); - // Bind to view for key and mouse events - // Add row processor which adds collapsed class - me.bindView(view); - view.addRowTpl(me.addCollapsedCls).rowExpander = me; - // If the owning grid is lockable, ensure the collapsed class is applied to the locked side by adding a row processor. - if (ownerLockable) { - me.addExpander(ownerLockable.lockedGrid.headerCt.items.getCount() ? ownerLockable.lockedGrid : grid); - // If our client grid part of a lockable grid, we listen to its ownerLockable's beforereconfigure - lockedView = ownerLockable.lockedGrid.getView(); - // Bind to locked view for key and mouse events - // Add row processor which adds collapsed class - me.bindView(lockedView); - lockedView.addRowTpl(me.addCollapsedCls).rowExpander = me; - ownerLockable.mon(ownerLockable, { - processcolumns: me.onLockableProcessColumns, - lockcolumn: me.onColumnLock, - unlockcolumn: me.onColumnUnlock, - scope: me - }); - // Process items added. - // It may be a re-rendering by the buffered renderer of an expanded item. - // If so, schedule a syncRowHeights call. - me.viewListeners = view.on({ - itemadd: me.onItemAdd, - scope: me - }); - } else { - me.addExpander(grid); - grid.on('beforereconfigure', me.beforeReconfigure, me); - } - }, - onItemAdd: function(newRecords, startIndex, newItems) { - var me = this, - ownerLockable = me.grid.ownerLockable, - lockableSyncRowHeights = me.lockableSyncRowHeights || (me.lockableSyncRowHeights = Ext.Function.createAnimationFrame(ownerLockable.syncRowHeights, ownerLockable)), - len = newItems.length, - i; - // If any added items are expanded, we will need a syncRowHeights call on next animation frame - for (i = 0; i < len; i++) { - if (!Ext.fly(newItems[i]).hasCls(me.rowCollapsedCls)) { - lockableSyncRowHeights(); - return; - } - } - }, - beforeReconfigure: function(grid, store, columns, oldStore, oldColumns) { - var me = this; - if (me.viewListeners) { - me.viewListeners.destroy(); - } - if (columns) { - me.expanderColumn = new Ext.grid.Column(me.getHeaderConfig()); - columns.unshift(me.expanderColumn); - } - }, - onLockableProcessColumns: function(lockable, lockedHeaders, normalHeaders) { - this.addExpander(lockedHeaders.length ? lockable.lockedGrid : lockable.normalGrid); - }, - /** - * @private - * Inject the expander column into the correct grid. - * - * If we are expanding the normal side of a lockable grid, poke the column into the locked side if the locked side has columns - */ - addExpander: function(expanderGrid) { - var me = this; - me.grid = expanderGrid; - me.expanderColumn = expanderGrid.headerCt.insert(0, me.getHeaderConfig()); - // If a CheckboxModel, it must now put its checkbox in at position one because this - // cell always gets in at position zero, and spans 2 columns. - expanderGrid.getSelectionModel().injectCheckbox = 1; - }, - getRowBodyFeatureData: function(record, idx, rowValues) { - var me = this; - me.self.prototype.setupRowData.apply(me, arguments); - rowValues.rowBody = me.getRowBodyContents(rowValues); - rowValues.rowBodyCls = me.recordsExpanded[record.internalId] ? '' : me.rowBodyHiddenCls; - }, - setup: function(rows, rowValues) { - var me = this, - lockable = me.grid.ownerLockable; - me.self.prototype.setup.apply(me, arguments); - // If we are lockable, and we are setting up the side which has the expander column, it is row spanning so we don't have to colspan it - if (lockable && Ext.Array.indexOf(me.grid.columnManager.getColumns(), me.rowExpander.expanderColumn) !== -1) { - rowValues.rowBodyColspan -= 1; - } - }, - bindView: function(view) { - view.on('itemkeydown', this.onKeyDown, this); - if (this.expandOnDblClick) { - view.on('itemdblclick', this.onDblClick, this); - } - }, - onKeyDown: function(view, record, row, rowIdx, e) { - var me = this, - key = e.getKey(), - pos = view.getNavigationModel().getPosition(), - isCollapsed; - if (pos) { - row = Ext.fly(row); - isCollapsed = row.hasCls(me.rowCollapsedCls); - // + key on collapsed or - key on expanded - if (((key === 107 || (key === 187 && e.shiftKey)) && isCollapsed) || ((key === 109 || key === 189) && !isCollapsed)) { - me.toggleRow(rowIdx, record); - } - } - }, - onDblClick: function(view, record, row, rowIdx, e) { - this.toggleRow(rowIdx, record); - }, - toggleRow: function(rowIdx, record) { - var me = this, - view = me.view, - bufferedRenderer = view.bufferedRenderer, - scroller = view.getScrollable(), - fireView = view, - rowNode = view.getNode(rowIdx), - normalRow = Ext.fly(rowNode), - lockedRow, - nextBd = normalRow.down(me.rowBodyTrSelector, true), - wasCollapsed = normalRow.hasCls(me.rowCollapsedCls), - addOrRemoveCls = wasCollapsed ? 'removeCls' : 'addCls', - // The expander column should be rowSpan="2" only when the expander is expanded - rowSpan = wasCollapsed ? 2 : 1, - ownerLockable = me.grid.ownerLockable, - expanderCell; - normalRow[addOrRemoveCls](me.rowCollapsedCls); - Ext.fly(nextBd)[addOrRemoveCls](me.rowBodyHiddenCls); - me.recordsExpanded[record.internalId] = wasCollapsed; - // Sync the collapsed/hidden classes on the locked side - if (me.grid.ownerLockable) { - // It's the top level grid's LockingView that does the firing when there's a lockable assembly involved. - fireView = ownerLockable.getView(); - // Only attempt to toggle lockable side if it is visible. - if (ownerLockable.lockedGrid.isVisible()) { - view = ownerLockable.view.lockedGrid.view; - // Process the locked side. - lockedRow = Ext.fly(view.getNode(rowIdx)); - // Just because the grid is locked, doesn't mean we'll necessarily have a locked row. - if (lockedRow) { - lockedRow[addOrRemoveCls](me.rowCollapsedCls); - // If there is a template for expander content in the locked side, toggle that side too - nextBd = lockedRow.down(me.rowBodyTrSelector, true); - Ext.fly(nextBd)[addOrRemoveCls](me.rowBodyHiddenCls); - } - } - } - // Here is where we set the rowSpan on this plugin's row expander cell. - // It should be rowSpan="2" only when the expander row is visible. - if (me.expanderColumn) { - expanderCell = Ext.fly(view.getRow(rowIdx)).down(me.expanderColumn.getCellSelector(), true); - if (expanderCell) { - expanderCell.rowSpan = rowSpan; - } - } - fireView.fireEvent(wasCollapsed ? 'expandbody' : 'collapsebody', rowNode, record, nextBd); - // Layout needed of we are shrinkwrapping height, or there are locked/unlocked sides to sync - // Will sync the expander row heights between locked and normal sides - if (view.getSizeModel().height.shrinkWrap || ownerLockable) { - view.refreshSize(true); - } - // If we are using the touch scroller, ensure that the scroller knows about - // the correct scrollable range - if (scroller) { - if (bufferedRenderer) { - bufferedRenderer.refreshSize(); - } else { - scroller.refresh(true); - } - } - }, - // Called from TableLayout.finishedLayout - syncRowHeights: function(lockedItem, normalItem) { - var me = this, - lockedBd = Ext.fly(lockedItem).down(me.rowBodyTrSelector), - normalBd = Ext.fly(normalItem).down(me.rowBodyTrSelector), - lockedHeight, normalHeight; - // If expanded, we have to ensure expander row heights are synched - if (normalBd.isVisible()) { - // If heights are different, expand the smallest one - if ((lockedHeight = lockedBd.getHeight()) !== (normalHeight = normalBd.getHeight())) { - if (lockedHeight > normalHeight) { - normalBd.setHeight(lockedHeight); - } else { - lockedBd.setHeight(normalHeight); - } - } - } else // When not expanded we do not control the heights - { - lockedBd.dom.style.height = normalBd.dom.style.height = ''; - } - }, - onColumnUnlock: function(lockable, column) { - var me = this, - lockedColumns; - lockable = me.grid.ownerLockable; - lockedColumns = lockable.lockedGrid.visibleColumnManager.getColumns(); - // User has unlocked all columns and left only the expander column in the locked side. - if (lockedColumns.length === 1) { - if (lockedColumns[0] === me.expanderColumn) { - lockable.unlock(me.expanderColumn); - me.grid = lockable.normalGrid; - } else { - lockable.lock(me.expanderColumn, 0); - } - } - }, - onColumnLock: function(lockable, column) { - var me = this, - lockedColumns, lockedGrid; - lockable = me.grid.ownerLockable; - lockedColumns = lockable.lockedGrid.visibleColumnManager.getColumns(); - // User has unlocked all columns and left only the expander column in the locked side. - if (lockedColumns.length === 1) { - me.grid = lockedGrid = lockable.lockedGrid; - lockedGrid.headerCt.insert(0, me.expanderColumn); - } - }, - getHeaderConfig: function() { - var me = this, - lockable = me.grid.ownerLockable; - return { - width: me.headerWidth, - ignoreExport: true, - lockable: false, - autoLock: true, - sortable: false, - resizable: false, - draggable: false, - hideable: false, - menuDisabled: true, - tdCls: Ext.baseCSSPrefix + 'grid-cell-special', - innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-row-expander', - renderer: function() { - return ''; - }, - processEvent: function(type, view, cell, rowIndex, cellIndex, e, record) { - if ((type === "click" && e.getTarget('.' + Ext.baseCSSPrefix + 'grid-row-expander')) || (type === 'keydown' && e.getKey() === e.SPACE)) { - me.toggleRow(rowIndex, record); - return me.selectRowOnExpand; - } - }, - // This column always migrates to the locked side if the locked side is visible. - // It has to report this correctly so that editors can position things correctly - isLocked: function() { - return lockable && (lockable.lockedGrid.isVisible() || this.locked); - }, - // In an editor, this shows nothing. - editRenderer: function() { - return ' '; - } - }; - } -}); - -/** - * A specialized grid implementation intended to mimic the traditional property grid as typically seen in - * development IDEs. Each row in the grid represents a property of some object, and the data is stored - * as a set of name/value pairs in {@link Ext.grid.property.Property Properties}. By default, the editors - * shown are inferred from the data in the cell. More control over this can be specified by using the - * {@link #sourceConfig} option. Example usage: - * - * @example - * Ext.create('Ext.grid.property.Grid', { - * title: 'Properties Grid', - * width: 300, - * renderTo: Ext.getBody(), - * source: { - * "(name)": "My Object", - * "Created": Ext.Date.parse('10/15/2006', 'm/d/Y'), - * "Available": false, - * "Version": 0.01, - * "Description": "A test object" - * } - * }); - */ -Ext.define('Ext.grid.property.Grid', { - extend: 'Ext.grid.Panel', - alias: 'widget.propertygrid', - alternateClassName: 'Ext.grid.PropertyGrid', - uses: [ - 'Ext.grid.plugin.CellEditing', - 'Ext.grid.property.Store', - 'Ext.grid.property.HeaderContainer', - 'Ext.XTemplate', - 'Ext.grid.CellEditor', - 'Ext.form.field.Date', - 'Ext.form.field.Text', - 'Ext.form.field.Number', - 'Ext.form.field.ComboBox' - ], - /** - * @cfg {Object} sourceConfig - * This option allows various configurations to be set for each field in the property grid. - * None of these configurations are required - * - * ####displayName - * A custom name to appear as label for this field. If specified, the display name will be shown - * in the name column instead of the property name. Example usage: - * - * new Ext.grid.property.Grid({ - * source: { - * clientIsAvailable: true - * }, - * sourceConfig: { - * clientIsAvailable: { - * // Custom name different to the field - * displayName: 'Available' - * } - * } - * }); - * - * ####renderer - * A function used to transform the underlying value before it is displayed in the grid. - * By default, the grid supports strongly-typed rendering of strings, dates, numbers and booleans using built-in form editors, - * but any custom type can be supported and associated with the type of the value. Example usage: - * - * new Ext.grid.property.Grid({ - * source: { - * clientIsAvailable: true - * }, - * sourceConfig: { - * clientIsAvailable: { - * // Custom renderer to change the color based on the value - * renderer: function(v){ - * var color = v ? 'green' : 'red'; - * return '' + v + ''; - * } - * } - * } - * }); - * - * ####type - * Used to explicitly specify the editor type for a particular value. By default, the type is - * automatically inferred from the value. See {@link #inferTypes}. Accepted values are: - * - * - 'date' - * - 'boolean' - * - 'number' - * - 'string' - * - * For more advanced control an editor configuration can be passed (see the next section). - * Example usage: - * - * new Ext.grid.property.Grid({ - * source: { - * attending: null - * }, - * sourceConfig: { - * attending: { - * // Force the type to be a numberfield, a null value would otherwise default to a textfield - * type: 'number' - * } - * } - * }); - * - * ####editor - * Allows the grid to support additional types of editable fields. By default, the grid supports strongly-typed editing - * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and - * associated with a custom input control by specifying a custom editor. Example usage - * - * new Ext.grid.property.Grid({ - * // Data object containing properties to edit - * source: { - * evtStart: '10:00 AM' - * }, - * - * sourceConfig: { - * evtStart: { - * editor: Ext.create('Ext.form.field.Time', {selectOnFocus: true}), - * displayName: 'Start Time' - * } - * } - * }); - */ - /** - * @cfg {Object} propertyNames - * An object containing custom property name/display name pairs. - * If specified, the display name will be shown in the name column instead of the property name. - * @deprecated See {@link #sourceConfig} displayName - */ - /** - * @cfg {Object} source - * A data object to use as the data source of the grid (see {@link #setSource} for details). - */ - /** - * @cfg {Object} customEditors - * An object containing name/value pairs of custom editor type definitions that allow - * the grid to support additional types of editable fields. By default, the grid supports strongly-typed editing - * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and - * associated with a custom input control by specifying a custom editor. The name of the editor - * type should correspond with the name of the property that will use the editor. Example usage: - * - * var grid = new Ext.grid.property.Grid({ - * - * // Custom editors for certain property names - * customEditors: { - * evtStart: Ext.create('Ext.form.TimeField', {selectOnFocus: true}) - * }, - * - * // Displayed name for property names in the source - * propertyNames: { - * evtStart: 'Start Time' - * }, - * - * // Data object containing properties to edit - * source: { - * evtStart: '10:00 AM' - * } - * }); - * @deprecated See {@link #sourceConfig} editor - */ - /** - * @cfg {Object} customRenderers - * An object containing name/value pairs of custom renderer type definitions that allow - * the grid to support custom rendering of fields. By default, the grid supports strongly-typed rendering - * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and - * associated with the type of the value. The name of the renderer type should correspond with the name of the property - * that it will render. Example usage: - * - * var grid = Ext.create('Ext.grid.property.Grid', { - * customRenderers: { - * Available: function(v){ - * if (v) { - * return 'Yes'; - * } else { - * return 'No'; - * } - * } - * }, - * source: { - * Available: true - * } - * }); - * @deprecated See {@link #sourceConfig} renderer - */ - /** - * @cfg {String} valueField - * The name of the field from the property store to use as the value field name. - * This may be useful if you do not configure the property Grid from an object, but use your own store configuration. - */ - valueField: 'value', - /** - * @cfg {String} nameField - * The name of the field from the property store to use as the property field name. - * This may be useful if you do not configure the property Grid from an object, but use your own store configuration. - */ - nameField: 'name', - /** - * @cfg {Boolean} inferTypes - * True to automatically infer the {@link #sourceConfig type} based on the initial value passed - * for each field. This ensures the editor remains the correct type even if the value is blanked - * and becomes empty. - */ - inferTypes: true, - /** - * @cfg {Number/String} [nameColumnWidth=115] - * Specify the width for the name column. The value column will take any remaining space. - */ - // private config overrides - enableColumnMove: false, - columnLines: true, - stripeRows: false, - trackMouseOver: false, - clicksToEdit: 1, - enableHdMenu: false, - gridCls: Ext.baseCSSPrefix + 'property-grid', - /** - * @event beforepropertychange - * Fires before a property value changes. Handlers can return false to cancel the property change - * (this will internally call {@link Ext.data.Model#reject} on the property's record). - * @param {Object} source The source data object for the grid (corresponds to the same object passed in - * as the {@link #source} config property). - * @param {String} recordId The record's id in the data store - * @param {Object} value The current edited property value - * @param {Object} oldValue The original property value prior to editing - */ - /** - * @event propertychange - * Fires after a property value has changed. - * @param {Object} source The source data object for the grid (corresponds to the same object passed in - * as the {@link #source} config property). - * @param {String} recordId The record's id in the data store - * @param {Object} value The current edited property value - * @param {Object} oldValue The original property value prior to editing - */ - initComponent: function() { - var me = this; - me.source = me.source || {}; - me.addCls(me.gridCls); - me.plugins = me.plugins || []; - // Enable cell editing. Inject a custom startEdit which always edits column 1 regardless of which column was clicked. - me.plugins.push(new Ext.grid.plugin.CellEditing({ - clicksToEdit: me.clicksToEdit, - // Inject a startEdit which always edits the value column - startEdit: function(record, column) { - // Maintainer: Do not change this 'this' to 'me'! It is the CellEditing object's own scope. - return this.self.prototype.startEdit.call(this, record, me.valueColumn); - } - })); - me.selModel = { - type: 'cellmodel', - onCellSelect: function(position) { - // We are only allowed to select the value column. - position.column = me.valueColumn; - position.colIdx = me.valueColumn.getVisibleIndex(); - return this.self.prototype.onCellSelect.call(this, position); - } - }; - me.sourceConfig = Ext.apply({}, me.sourceConfig); - // Create a property.Store from the source object unless configured with a store - if (!me.store) { - me.propStore = me.store = new Ext.grid.property.Store(me, me.source); - } - me.configure(me.sourceConfig); - if (me.sortableColumns) { - me.store.sort('name', 'ASC'); - } - me.columns = new Ext.grid.property.HeaderContainer(me, me.store); - me.callParent(); - // Inject a custom implementation of walkCells which only goes up or down - me.getView().walkCells = this.walkCells; - // Set up our default editor set for the 4 atomic data types - me.editors = { - 'date': new Ext.grid.CellEditor({ - field: new Ext.form.field.Date({ - selectOnFocus: true - }) - }), - 'string': new Ext.grid.CellEditor({ - field: new Ext.form.field.Text({ - selectOnFocus: true - }) - }), - 'number': new Ext.grid.CellEditor({ - field: new Ext.form.field.Number({ - selectOnFocus: true - }) - }), - 'boolean': new Ext.grid.CellEditor({ - field: new Ext.form.field.ComboBox({ - editable: false, - store: [ - [ - true, - me.headerCt.trueText - ], - [ - false, - me.headerCt.falseText - ] - ] - }) - }) - }; - // Track changes to the data so we can fire our events. - me.store.on('update', me.onUpdate, me); - }, - configure: function(config) { - var me = this, - store = me.store, - i = 0, - len = me.store.getCount(), - nameField = me.nameField, - valueField = me.valueField, - name, value, rec, type; - me.configureLegacy(config); - if (me.inferTypes) { - for (; i < len; ++i) { - rec = store.getAt(i); - name = rec.get(nameField); - if (!me.getConfigProp(name, 'type')) { - value = rec.get(valueField); - if (Ext.isDate(value)) { - type = 'date'; - } else if (Ext.isNumber(value)) { - type = 'number'; - } else if (Ext.isBoolean(value)) { - type = 'boolean'; - } else { - type = 'string'; - } - me.setConfigProp(name, 'type', type); - } - } - } - }, - getConfigProp: function(fieldName, key, defaultValue) { - var config = this.sourceConfig[fieldName], - out; - if (config) { - out = config[key]; - } - return out || defaultValue; - }, - setConfigProp: function(fieldName, key, value) { - var sourceCfg = this.sourceConfig, - o = sourceCfg[fieldName]; - if (!o) { - o = sourceCfg[fieldName] = { - __copied: true - }; - } else if (!o.__copied) { - o = Ext.apply({ - __copied: true - }, o); - sourceCfg[fieldName] = o; - } - o[key] = value; - return value; - }, - // to be deprecated in 4.2 - configureLegacy: function(config) { - var me = this; - me.copyLegacyObject(config, me.customRenderers, 'renderer'); - me.copyLegacyObject(config, me.customEditors, 'editor'); - me.copyLegacyObject(config, me.propertyNames, 'displayName'); - // exclude types since it's new - if (me.customRenderers || me.customEditors || me.propertyNames) { - if (Ext.global.console && Ext.global.console.warn) { - Ext.global.console.warn(this.$className, 'customRenderers, customEditors & propertyNames have been consolidated into a new config, see "sourceConfig". These configurations will be deprecated.'); - } - } - }, - copyLegacyObject: function(config, o, keyName) { - var key; - for (key in o) { - if (o.hasOwnProperty(key)) { - if (!config[key]) { - config[key] = {}; - } - config[key][keyName] = o[key]; - } - } - }, - /** - * @private - */ - onUpdate: function(store, record, operation) { - var me = this, - v, oldValue; - if (me.rendered && operation === Ext.data.Model.EDIT) { - v = record.get(me.valueField); - oldValue = record.modified.value; - if (me.fireEvent('beforepropertychange', me.source, record.getId(), v, oldValue) !== false) { - if (me.source) { - me.source[record.getId()] = v; - } - record.commit(); - me.fireEvent('propertychange', me.source, record.getId(), v, oldValue); - } else { - record.reject(); - } - } - }, - // Custom implementation of walkCells which only goes up and down. - // Runs in the scope of the TableView - walkCells: function(pos, direction, e, preventWrap, verifierFn, scope) { - var me = this, - valueColumn = me.ownerCt.valueColumn; - if (direction === 'left') { - direction = 'up'; - } else if (direction === 'right') { - direction = 'down'; - } - pos = Ext.view.Table.prototype.walkCells.call(me, pos, direction, e, preventWrap, verifierFn, scope); - // We are only allowed to navigate to the value column. - pos.column = valueColumn; - pos.colIdx = valueColumn.getVisibleIndex(); - return pos; - }, - /** - * @private - * Returns the correct editor type for the property type, or a custom one keyed by the property name - */ - getCellEditor: function(record, column) { - var me = this, - propName = record.get(me.nameField), - val = record.get(me.valueField), - editor = me.getConfigProp(propName, 'editor'), - type = me.getConfigProp(propName, 'type'), - editors = me.editors, - field; - // A custom editor was found. If not already wrapped with a CellEditor, wrap it, and stash it back - // If it's not even a Field, just a config object, instantiate it before wrapping it. - if (editor) { - if (!(editor instanceof Ext.grid.CellEditor)) { - if (!(editor instanceof Ext.form.field.Base)) { - editor = Ext.ComponentManager.create(editor, 'textfield'); - } - editor = me.setConfigProp(propName, 'editor', new Ext.grid.CellEditor({ - field: editor - })); - } - } else if (type) { - switch (type) { - case 'date': - editor = editors.date; - break; - case 'number': - editor = editors.number; - break; - case 'boolean': - // Cannot be ".boolean" - YUI hates using reserved words like that - editor = me.editors['boolean']; - // jshint ignore:line - break; - default: - editor = editors.string; - } - } else if (Ext.isDate(val)) { - editor = editors.date; - } else if (Ext.isNumber(val)) { - editor = editors.number; - } else if (Ext.isBoolean(val)) { - // Cannot be ".boolean" - YUI hates using reserved words like that - editor = editors['boolean']; - } else // jshint ignore:line - { - editor = editors.string; - } - field = editor.field; - if (field && field.ui === 'default' && !field.hasOwnProperty('ui')) { - field.ui = me.editingPlugin.defaultFieldUI; - } - // Give the editor a unique ID because the CellEditing plugin caches them - editor.editorId = propName; - editor.field.column = me.valueColumn; - return editor; - }, - beforeDestroy: function() { - var me = this; - me.callParent(); - me.destroyEditors(me.editors); - me.destroyEditors(me.customEditors); - delete me.source; - }, - destroyEditors: function(editors) { - for (var ed in editors) { - if (editors.hasOwnProperty(ed)) { - Ext.destroy(editors[ed]); - } - } - }, - /** - * Sets the source data object containing the property data. The data object can contain one or more name/value - * pairs representing all of the properties of an object to display in the grid, and this data will automatically - * be loaded into the grid's {@link #store}. The values should be supplied in the proper data type if needed, - * otherwise string type will be assumed. If the grid already contains data, this method will replace any - * existing data. See also the {@link #source} config value. Example usage: - * - * grid.setSource({ - * "(name)": "My Object", - * "Created": Ext.Date.parse('10/15/2006', 'm/d/Y'), // date type - * "Available": false, // boolean type - * "Version": .01, // decimal type - * "Description": "A test object" - * }); - * - * @param {Object} source The data object. - * @param {Object} [sourceConfig] A new {@link #sourceConfig object}. If this argument is not passed - * the current configuration will be re-used. To reset the config, pass `null` or an empty object literal. - */ - setSource: function(source, sourceConfig) { - var me = this; - me.source = source; - if (sourceConfig !== undefined) { - me.sourceConfig = Ext.apply({}, sourceConfig); - me.configure(me.sourceConfig); - } - me.propStore.setSource(source); - }, - /** - * Gets the source data object containing the property data. See {@link #setSource} for details regarding the - * format of the data object. - * @return {Object} The data object. - */ - getSource: function() { - return this.propStore.getSource(); - }, - /** - * Gets the value of a property. - * @param {String} prop The name of the property. - * @return {Object} The property value. `null` if there is no value. - * - * @since 5.1.1 - */ - getProperty: function(prop) { - return this.propStore.getProperty(prop); - }, - /** - * Sets the value of a property. - * @param {String} prop The name of the property to set. - * @param {Object} value The value to test. - * @param {Boolean} [create=false] `true` to create the property if it doesn't already exist. - */ - setProperty: function(prop, value, create) { - this.propStore.setValue(prop, value, create); - }, - /** - * Removes a property from the grid. - * @param {String} prop The name of the property to remove. - */ - removeProperty: function(prop) { - this.propStore.remove(prop); - } -}); -/** - * @cfg {Object} store - * @private - */ -/** - * @cfg {Object} columns - * @private - */ - -/** - * A custom HeaderContainer for the {@link Ext.grid.property.Grid}. - * Generally it should not need to be used directly. - */ -Ext.define('Ext.grid.property.HeaderContainer', { - extend: 'Ext.grid.header.Container', - alternateClassName: 'Ext.grid.PropertyColumnModel', - nameWidth: 115, - // - nameText: 'Name', - // - // - valueText: 'Value', - // - // - dateFormat: 'm/j/Y', - // - // - trueText: 'true', - // - // - falseText: 'false', - // - /** - * @private - */ - nameColumnCls: Ext.baseCSSPrefix + 'grid-property-name', - nameColumnInnerCls: Ext.baseCSSPrefix + 'grid-cell-inner-property-name', - /** - * Creates new HeaderContainer. - * @param {Ext.grid.property.Grid} grid The grid this store will be bound to - * @param {Object} source The source data config object - */ - constructor: function(grid, store) { - var me = this; - me.grid = grid; - me.store = store; - me.callParent([ - { - isRootHeader: true, - enableColumnResize: Ext.isDefined(grid.enableColumnResize) ? grid.enableColumnResize : me.enableColumnResize, - enableColumnMove: Ext.isDefined(grid.enableColumnMove) ? grid.enableColumnMove : me.enableColumnMove, - items: [ - { - header: me.nameText, - width: grid.nameColumnWidth || me.nameWidth, - sortable: grid.sortableColumns, - dataIndex: grid.nameField, - scope: me, - renderer: me.renderProp, - itemId: grid.nameField, - menuDisabled: true, - tdCls: me.nameColumnCls, - innerCls: me.nameColumnInnerCls - }, - { - header: me.valueText, - scope: me, - renderer: me.renderCell, - getEditor: me.getCellEditor.bind(me), - sortable: grid.sortableColumns, - flex: 1, - fixed: true, - dataIndex: grid.valueField, - itemId: grid.valueField, - menuDisabled: true - } - ] - } - ]); - // PropertyGrid needs to know which column is the editable "value" column. - me.grid.valueColumn = me.items.getAt(1); - }, - getCellEditor: function(record) { - return this.grid.getCellEditor(record, this); - }, - /** - * @private - * Render a property name cell - */ - renderProp: function(v) { - return this.getPropertyName(v); - }, - /** - * @private - * Render a property value cell - */ - renderCell: function(val, meta, rec) { - var me = this, - grid = me.grid, - renderer = grid.getConfigProp(rec.get(grid.nameField), 'renderer'), - result = val; - if (renderer) { - return renderer.apply(me, arguments); - } - if (Ext.isDate(val)) { - result = me.renderDate(val); - } else if (Ext.isBoolean(val)) { - result = me.renderBool(val); - } - return Ext.util.Format.htmlEncode(result); - }, - /** - * @private - */ - renderDate: Ext.util.Format.date, - /** - * @private - */ - renderBool: function(bVal) { - return this[bVal ? 'trueText' : 'falseText']; - }, - /** - * @private - * Renders custom property names instead of raw names if defined in the Grid - */ - getPropertyName: function(name) { - return this.grid.getConfigProp(name, 'displayName', name); - } -}); - -/** - * A specific {@link Ext.data.Model} type that represents a name/value pair and is made to work with the - * {@link Ext.grid.property.Grid}. Typically, Properties do not need to be created directly as they can be - * created implicitly by simply using the appropriate data configs either via the - * {@link Ext.grid.property.Grid#source} config property or by calling {@link Ext.grid.property.Grid#setSource}. - * However, if the need arises, these records can also be created explicitly as shown below. Example usage: - * - * var rec = new Ext.grid.property.Property({ - * name: 'birthday', - * value: Ext.Date.parse('17/06/1962', 'd/m/Y') - * }); - * // Add record to an already populated grid - * grid.store.addSorted(rec); - * - * @constructor - * Creates new property. - * @param {Object} config A data object in the format: - * @param {String/String[]} config.name A name or names for the property. - * @param {Mixed/Mixed[]} config.value A value or values for the property. - * The specified value's type will be read automatically by the grid to determine the type of editor to use when - * displaying it. - * @return {Object} - */ -Ext.define('Ext.grid.property.Property', { - extend: 'Ext.data.Model', - alternateClassName: 'Ext.PropGridProperty', - fields: [ - { - name: 'name', - type: 'string' - }, - { - name: 'value' - } - ], - idProperty: 'name', - constructor: function(data, value) { - if (!Ext.isObject(data)) { - data = { - name: data, - value: value - }; - } - this.callParent([ - data - ]); - } -}); - -/** - * @private - * Custom reader for property grid data - */ -Ext.define('Ext.grid.property.Reader', { - extend: 'Ext.data.reader.Reader', - successProperty: null, - totalProperty: null, - messageProperty: null, - read: function(dataObject) { - return this.readRecords(dataObject); - }, - readRecords: function(dataObject) { - var Model = this.getModel(), - result = { - records: [], - success: true - }, - val, propName; - for (propName in dataObject) { - if (dataObject.hasOwnProperty(propName)) { - val = dataObject[propName]; - if (this.isEditableValue(val)) { - result.records.push(new Model({ - name: propName, - value: val - })); - } - } - } - result.total = result.count = result.records.length; - return new Ext.data.ResultSet(result); - }, - /** - * @private - */ - isEditableValue: function(val) { - return Ext.isPrimitive(val) || Ext.isDate(val) || val === null; - } -}); - -/** - * A custom {@link Ext.data.Store} for the {@link Ext.grid.property.Grid}. This class handles the mapping - * between the custom data source objects supported by the grid and the {@link Ext.grid.property.Property} format - * used by the {@link Ext.data.Store} base class. - */ -Ext.define('Ext.grid.property.Store', { - extend: 'Ext.data.Store', - alternateClassName: 'Ext.grid.PropertyStore', - remoteSort: true, - requires: [ - 'Ext.grid.property.Reader', - 'Ext.data.proxy.Memory', - 'Ext.grid.property.Property' - ], - /** - * Creates new property store. - * @param {Ext.grid.Panel} grid The grid this store will be bound to - * @param {Object} source The source data config object - */ - constructor: function(grid, source) { - var me = this; - me.grid = grid; - me.source = source; - me.callParent([ - { - data: source, - model: Ext.grid.property.Property, - proxy: me.getProxy() - } - ]); - }, - // Return a singleton, customized Proxy object which configures itself with a custom Reader - getProxy: function() { - var proxy = this.proxy; - if (!proxy) { - proxy = this.proxy = new Ext.data.proxy.Memory({ - model: Ext.grid.property.Property, - reader: this.getReader() - }); - } - return proxy; - }, - // Return a singleton, customized Reader object which reads Ext.grid.property.Property records from an object. - getReader: function() { - var reader = this.reader; - if (!reader) { - reader = this.reader = new Ext.grid.property.Reader({ - model: Ext.grid.property.Property - }); - } - return reader; - }, - // @protected - // Should only be called by the grid. Use grid.setSource instead. - setSource: function(dataObject) { - var me = this; - me.source = dataObject; - me.suspendEvents(); - me.removeAll(); - me.getProxy().setData(dataObject); - me.load(); - me.resumeEvents(); - me.fireEvent('datachanged', me); - me.fireEvent('refresh', me); - }, - /** - * @private - */ - getProperty: function(row) { - var rec = Ext.isNumber(row) ? this.getAt(row) : this.getById(row), - ret = null; - if (rec) { - ret = rec.get('value'); - } - return ret; - }, - /** - * @private - */ - setValue: function(prop, value, create) { - var me = this, - rec = me.getRec(prop); - if (rec) { - rec.set('value', value); - me.source[prop] = value; - } else if (create) { - // only create if specified. - me.source[prop] = value; - rec = new Ext.grid.property.Property({ - name: prop, - value: value - }, prop); - me.add(rec); - } - }, - /** - * @private - */ - remove: function(prop) { - var rec = this.getRec(prop); - if (rec) { - this.callParent([ - rec - ]); - delete this.source[prop]; - } - }, - /** - * @private - */ - getRec: function(prop) { - return this.getById(prop); - }, - /** - * @protected - * Should only be called by the grid. Use grid.getSource instead. - */ - getSource: function() { - return this.source; - }, - onDestroy: function() { - Ext.destroy(this.reader, this.proxy); - this.callParent(); - } -}); - -/** - * Base class for selections which may be of three subtypes: - * - * - {@link Ext.grid.selection.Cells Cells} A rectangular range of cells defined by a start - * record/column and an end record/column. - * - {@link Ext.grid.selection.Rows Rows} An array of records. - * - {@link Ext.grid.selection.Columns Columns} An array of columns in which all records - * are included. - * - * @since 5.1.0 - */ -Ext.define('Ext.grid.selection.Selection', { - constructor: function(view) { - if (!view || !(view.isTableView || view.isLockingView)) { - Ext.raise('Selection must be created for a given TableView or LockingView'); - } - // We use the topmost (possible Ext.locking.View) view - this.view = view.ownerGrid.view; - } -}); -/** - * Clones this selection object. - * @return {Ext.grid.selection.Selection} A clone of this instance. - * @method clone - */ -/** - * Clears the selection represented by this selection object. - * @private - * @method clear - */ -/** - * Calls the passed function for each selected {@link Ext.data.Model record}. - * - * @param {Function} fn The function to call. If this returns `false`, the iteration is - * halted with no further calls. - * @param {Ext.data.Model} fn.record The current record. - * @param {Object} [scope] The context (`this` reference) in which the function is executed. - * Defaults to this Selection object. - * @method eachRow - */ -/** - * Calls the passed function for each selected cell from top left to bottom right - * iterating over columns within each row. - * - * @param {Function} fn The function to call. If this returns `false`, the iteration is - * halted with no further calls. - * @param {Ext.grid.CellContext} fn.context The CellContext representing the current cell. - * @param {Number} fn.columnIndex The column index of the current cell. - * @param {Number} fn.rowIndex The row index of the current cell. - * @param {Object} [scope] The context (`this` reference) in which `fn` is executed. - * Defaults to this Selection object. - * @method eachCell - */ -/** - * Calls the passed function for each selected column from left to right. - * - * @param {Function} fn The function to call. If this returns false, the iteration is - * halted with no further calls. - * @param {Ext.grid.column.Column} fn.column The current column. - * @param {Number} fn.columnIndex The index of the current column. *Note that in a - * locked grid, this is relative to the outermost grid encompassing both sides*. - * @param {Object} [scope] The context (`this` reference) in which `fn` is executed. - * Defaults to this Selection object. - * @method eachColumn - */ -/** - * Called when selection is completed. - * @method onSelectionFinish - * @private - */ - -/** - * A class which encapsulates a range of cells defining a selection in a grid. - * - * Note that when range start and end points are represented by an array, the - * order is traditional `x, y` order, that is column index followed by row index. - * @since 5.1.0 - */ -Ext.define('Ext.grid.selection.Cells', { - extend: 'Ext.grid.selection.Selection', - type: 'cells', - /** - * @property {Boolean} isCells - * This property indicates the this selection represents selected cells. - * @readonly - */ - isCells: true, - //------------------------------------------------------------------------- - // Base Selection API - clone: function() { - var me = this, - result = new me.self(me.view); - if (me.startCell) { - result.startCell = me.startCell.clone(); - result.endCell = me.endCell.clone(); - } - return result; - }, - /** - * Returns `true` if the passed {@link Ext.grid.CellContext cell context} is selected. - * @param {Ext.grid.CellContext} cellContext The cell context to test. - * @return {Boolean} `true` if the passed {@link Ext.grid.CellContext cell context} is selected. - */ - contains: function(cellContext) { - var range; - if (!cellContext || !cellContext.isCellContext) { - return false; - } - if (this.startCell) { - // get start and end rows in the range - range = this.getRowRange(); - if (cellContext.rowIdx >= range[0] && cellContext.rowIdx <= range[1]) { - // get start and end columns in the range - range = this.getColumnRange(); - return (cellContext.colIdx >= range[0] && cellContext.colIdx <= range[1]); - } - } - return false; - }, - eachRow: function(fn, scope) { - var me = this, - rowRange = me.getRowRange(), - context = new Ext.grid.CellContext(me.view), - rowIdx; - for (rowIdx = rowRange[0]; rowIdx <= rowRange[1]; rowIdx++) { - context.setRow(rowIdx); - if (fn.call(scope || me, context.record) === false) { - return; - } - } - }, - eachColumn: function(fn, scope) { - var me = this, - colRange = me.getColumnRange(), - context = new Ext.grid.CellContext(me.view), - colIdx; - for (colIdx = colRange[0]; colIdx <= colRange[1]; colIdx++) { - context.setColumn(colIdx); - if (fn.call(scope || me, context.column, colIdx) === false) { - return; - } - } - }, - eachCell: function(fn, scope) { - var me = this, - rowRange = me.getRowRange(), - colRange = me.getColumnRange(), - context = new Ext.grid.CellContext(me.view), - rowIdx, colIdx; - for (rowIdx = rowRange[0]; rowIdx <= rowRange[1]; rowIdx++) { - context.setRow(rowIdx); - for (colIdx = colRange[0]; colIdx <= colRange[1]; colIdx++) { - context.setColumn(colIdx); - if (fn.call(scope || me, context, colIdx, rowIdx) === false) { - return; - } - } - } - }, - /** - * @return {Number} The row index of the first row in the range or zero if no range. - */ - getFirstRowIndex: function() { - return this.startCell ? Math.min(this.startCell.rowIdx, this.endCell.rowIdx) : 0; - }, - /** - * @return {Number} The row index of the last row in the range or -1 if no range. - */ - getLastRowIndex: function() { - return this.startCell ? Math.max(this.startCell.rowIdx, this.endCell.rowIdx) : -1; - }, - /** - * @return {Number} The column index of the first column in the range or zero if no range. - */ - getFirstColumnIndex: function() { - return this.startCell ? Math.min(this.startCell.colIdx, this.endCell.colIdx) : 0; - }, - /** - * @return {Number} The column index of the last column in the range or -1 if no range. - */ - getLastColumnIndex: function() { - return this.startCell ? Math.max(this.startCell.colIdx, this.endCell.colIdx) : -1; - }, - //------------------------------------------------------------------------- - privates: { - /** - * @private - */ - clear: function() { - var me = this, - view = me.view; - me.eachCell(function(cellContext) { - view.onCellDeselect(cellContext); - }); - me.startCell = me.endCell = null; - }, - /** - * Used during drag/shift+downarrow range selection on start. - * @param {Ext.grid.CellContext} startCell The start cell of the cell drag selection. - * @private - */ - setRangeStart: function(startCell, endCell) { - // Must clone them. Users might use one instance and reconfigure it to navigate. - this.startCell = (this.endCell = startCell.clone()).clone(); - this.view.onCellSelect(startCell); - }, - /** - * Used during drag/shift+downarrow range selection on drag. - * @param {Ext.grid.CellContext} endCell The end cell of the cell drag selection. - * @private - */ - setRangeEnd: function(endCell) { - var me = this, - range, lastRange, rowStart, rowEnd, colStart, colEnd, rowIdx, colIdx, - view = me.view, - rows = view.all, - cell = new Ext.grid.CellContext(view), - maxColIdx = view.getVisibleColumnManager().getColumns().length - 1; - me.endCell = endCell.clone(); - range = me.getRange(); - lastRange = me.lastRange || range; - rowStart = Math.max(Math.min(range[0][1], lastRange[0][1]), rows.startIndex); - rowEnd = Math.min(Math.max(range[1][1], lastRange[1][1]), rows.endIndex); - colStart = Math.min(range[0][0], lastRange[0][0]); - colEnd = Math.min(Math.max(range[1][0], lastRange[1][0]), maxColIdx); - // Loop through the union of last range and current range - for (rowIdx = rowStart; rowIdx <= rowEnd; rowIdx++) { - for (colIdx = colStart; colIdx <= colEnd; colIdx++) { - cell.setPosition(rowIdx, colIdx); - // If we are outside the current range, deselect - if (rowIdx < range[0][1] || rowIdx > range[1][1] || colIdx < range[0][0] || colIdx > range[1][0]) { - view.onCellDeselect(cell); - } else { - view.onCellSelect(cell); - } - } - } - me.lastRange = range; - }, - extendRange: function(extensionVector) { - var me = this, - newEndCell; - if (extensionVector[extensionVector.type] < 0) { - newEndCell = me.endCell.clone().setPosition(me.getLastRowIndex(), me.getLastColumnIndex()); - me.startCell = extensionVector.start.clone(); - me.setRangeEnd(newEndCell); - me.view.getNavigationModel().setPosition(extensionVector.start); - } else { - me.startCell = me.startCell.setPosition(me.getFirstRowIndex(), me.getFirstColumnIndex()); - me.setRangeEnd(extensionVector.end); - me.view.getNavigationModel().setPosition(extensionVector.end); - } - }, - /** - * Returns the `[[x, y],[x,y]]` coordinates in top-left to bottom-right order - * of the current selection. - * - * If no selection, returns [[0, 0],[-1, -1]] so that an incrementing iteration - * will not execute. - * - * @return {Number[][]} - * @private - */ - getRange: function() { - return [ - [ - this.getFirstColumnIndex(), - this.getFirstRowIndex() - ], - [ - this.getLastColumnIndex(), - this.getLastRowIndex() - ] - ]; - }, - /** - * Returns the size of the selection rectangle. - * @return {Number} - * @private - */ - getRangeSize: function() { - return this.getCount(); - }, - /** - * Returns the number of cells selected. - * @return {Number} The nuimber of cells selected - * @private - */ - getCount: function() { - var range = this.getRange(); - return (range[1][0] - range[0][0] + 1) * (range[1][1] - range[0][1] + 1); - }, - /** - * @private - */ - selectAll: function() { - var me = this, - view = me.view; - me.clear(); - me.setRangeStart(new Ext.grid.CellContext(view).setPosition(0, 0)); - me.setRangeEnd(new Ext.grid.CellContext(view).setPosition(view.dataSource.getCount() - 1, view.getVisibleColumnManager().getColumns().length - 1)); - }, - /** - * @return {Boolean} - * @private - */ - isAllSelected: function() { - var start = this.rangeStart, - end = this.rangeEnd; - // All selected only if we encompass the entire store and every visible column - if (start) { - if (!start.colIdx && !start.rowIdx) { - return end.colIdx === end.view.getVisibleColumnManager().getColumns().length - 1 && end.rowIdx === end.view.dataSource.getCount - 1; - } - } - return false; - }, - /** - * @return {Number[]} The column range which encapsulates the range. - * @private - */ - getColumnRange: function() { - return [ - this.getFirstColumnIndex(), - this.getLastColumnIndex() - ]; - }, - /** - * Returns the row range which encapsulates the range - the view range that needs - * updating. - * @return {Number[]} - * @private - */ - getRowRange: function() { - return [ - this.getFirstRowIndex(), - this.getLastRowIndex() - ]; - }, - onSelectionFinish: function() { - var me = this; - if (me.getCount()) { - me.view.getSelectionModel().onSelectionFinish(me, new Ext.grid.CellContext(me.view).setPosition(me.getFirstRowIndex(), me.getFirstColumnIndex()), new Ext.grid.CellContext(me.view).setPosition(me.getLastRowIndex(), me.getLastColumnIndex())); - } else { - me.view.getSelectionModel().onSelectionFinish(me); - } - } - } -}); - -/** - * A class which encapsulates a range of columns defining a selection in a grid. - * @since 5.1.0 - */ -Ext.define('Ext.grid.selection.Columns', { - extend: 'Ext.grid.selection.Selection', - type: 'columns', - /** - * @property {Boolean} isColumns - * This property indicates the this selection represents selected columns. - * @readonly - */ - isColumns: true, - //------------------------------------------------------------------------- - // Base Selection API - clone: function() { - var me = this, - result = new me.self(me.view), - columns = me.selectedColumns; - if (columns) { - result.selectedColumns = Ext.Array.slice(columns); - } - return result; - }, - eachRow: function(fn, scope) { - var columns = this.selectedColumns; - if (columns && columns.length) { - this.view.dataSource.each(fn, scope || this); - } - }, - eachColumn: function(fn, scope) { - var me = this, - view = me.view, - columns = me.selectedColumns, - len, i, - context = new Ext.grid.CellContext(view); - if (columns) { - len = columns.length; - for (i = 0; i < len; i++) { - context.setColumn(columns[i]); - if (fn.call(scope || me, context.column, context.colIdx) === false) { - return false; - } - } - } - }, - eachCell: function(fn, scope) { - var me = this, - view = me.view, - columns = me.selectedColumns, - len, i, - context = new Ext.grid.CellContext(view); - if (columns) { - len = columns.length; - // Use Store#each instead of copying the entire dataset into an array and iterating that. - view.dataSource.each(function(record) { - context.setRow(record); - for (i = 0; i < len; i++) { - context.setColumn(columns[i]); - if (fn.call(scope || me, context, context.colIdx, context.rowIdx) === false) { - return false; - } - } - }); - } - }, - //------------------------------------------------------------------------- - // Methods unique to this type of Selection - /** - * Returns `true` if the passed {@link Ext.grid.column.Column column} is selected. - * @param {Ext.grid.column.Column} column The column to test. - * @return {Boolean} `true` if the passed {@link Ext.grid.column.Column column} is selected. - */ - contains: function(column) { - var selectedColumns = this.selectedColumns; - if (column && column.isColumn && selectedColumns && selectedColumns.length) { - return Ext.Array.contains(selectedColumns, column); - } - return false; - }, - /** - * Returns the number of columns selected. - * @return {Number} The number of columns selected. - */ - getCount: function() { - var selectedColumns = this.selectedColumns; - return selectedColumns ? selectedColumns.length : 0; - }, - /** - * Returns the columns selected. - * @return {Ext.grid.column.Column[]} The columns selected. - */ - getColumns: function() { - return this.selectedColumns || []; - }, - //------------------------------------------------------------------------- - privates: { - /** - * Adds the passed Column to the selection. - * @param {Ext.grid.column.Column} column - * @private - */ - add: function(column) { - if (!column.isColumn) { - Ext.raise('Column selection must be passed a grid Column header object'); - } - Ext.Array.include((this.selectedColumns || (this.selectedColumns = [])), column); - this.refreshColumns(column); - }, - /** - * @private - */ - clear: function() { - var me = this, - prevSelection = me.selectedColumns; - if (prevSelection && prevSelection.length) { - me.selectedColumns = []; - me.refreshColumns.apply(me, prevSelection); - } - }, - /** - * @return {Boolean} - * @private - */ - isAllSelected: function() { - var selectedColumns = this.selectedColumns; - // All selected means all columns, across both views if we are in a locking assembly. - return selectedColumns && selectedColumns.length === this.view.ownerGrid.getVisibleColumnManager().getColumns().length; - }, - /** - * @private - */ - refreshColumns: function(column) { - var me = this, - view = me.view, - rows = view.all, - rowIdx, - columns = arguments, - len = columns.length, - colIdx, - cellContext = new Ext.grid.CellContext(view), - selected = []; - if (view.rendered) { - for (colIdx = 0; colIdx < len; colIdx++) { - selected[colIdx] = me.contains(columns[colIdx]); - } - for (rowIdx = rows.startIndex; rowIdx <= rows.endIndex; rowIdx++) { - cellContext.setRow(rowIdx); - for (colIdx = 0; colIdx < len; colIdx++) { - // Note colIdx is not the column's visible index. setColumn must be passed the column object - cellContext.setColumn(columns[colIdx]); - if (selected[colIdx]) { - view.onCellSelect(cellContext); - } else { - view.onCellDeselect(cellContext); - } - } - } - } - }, - /** - * Removes the passed Column from the selection. - * @param {Ext.grid.column.Column} column - * @private - */ - remove: function(column) { - if (!column.isColumn) { - Ext.raise('Column selection must be passed a grid Column header object'); - } - if (this.selectedColumns) { - Ext.Array.remove(this.selectedColumns, column); - // Might be being called because of column removal/hiding. - // In which case the view will have selected cells removed, so no refresh needed. - if (column.getView() && column.isVisible()) { - this.refreshColumns(column); - } - } - }, - /** - * @private - */ - selectAll: function() { - var me = this; - me.clear(); - me.selectedColumns = me.view.getSelectionModel().lastContiguousColumnRange = me.view.getVisibleColumnManager().getColumns(); - me.refreshColumns.apply(me, me.selectedColumns); - }, - extendRange: function(extensionVector) { - var me = this, - columns = me.view.getVisibleColumnManager().getColumns(), - i; - for (i = extensionVector.start.colIdx; i <= extensionVector.end.colIdx; i++) { - me.add(columns[i]); - } - }, - onSelectionFinish: function() { - var me = this, - range = me.getContiguousSelection(); - if (range) { - me.view.getSelectionModel().onSelectionFinish(me, new Ext.grid.CellContext(me.view).setPosition(0, range[0]), new Ext.grid.CellContext(me.view).setPosition(me.view.dataSource.getCount() - 1, range[1])); - } else { - me.view.getSelectionModel().onSelectionFinish(me); - } - }, - /** - * @return {Array} `[startColumn, endColumn]` if the selection represents a visually contiguous set of columns. - * The SelectionReplicator is only enabled if there is a contiguous block. - * @private - */ - getContiguousSelection: function() { - var selection = Ext.Array.sort(this.selectedColumns, function(c1, c2) { - // Use index *in ownerGrid* so that a locking assembly can order columns correctly - return c1.getView().ownerGrid.getVisibleColumnManager().indexOf(c1) - c2.getView().ownerGrid.getVisibleColumnManager().indexOf(c2); - }), - len = selection.length, - i; - if (len) { - for (i = 1; i < len; i++) { - if (selection[i].getVisibleIndex() !== selection[i - 1].getVisibleIndex() + 1) { - return false; - } - } - return [ - selection[0], - selection[len - 1] - ]; - } - } - } -}); - -/** - * A plugin for use in grids which use the {@link Ext.grid.selection.SpreadsheetModel spreadsheet} selection model, - * with {@link Ext.grid.selection.SpreadsheetModel#extensible extensible} configured as `true` or `"y"`, meaning that - * the selection may be extended up or down using a draggable extension handle. - * - * This plugin propagates values from the selection into the extension area. - * - * If just *one* row is selected, the values in that row are replicated unchanged into the extension area. - * - * If more than one row is selected, the two rows closest to the selected block are taken to provide a numeric - * difference, and that difference is used to calculate the sequence of values all the way into the extension area. - * - */ -Ext.define('Ext.grid.selection.Replicator', { - extend: 'Ext.plugin.Abstract', - alias: 'plugin.selectionreplicator', - /** - * - * @property {Ext.grid.column.Column[]} columns - * An array of the columns encompassed by the selection block. This is gathered before {@link #replicateSelection} - * is called, so is available to subclasses which implement their own {@link #replicateSelection} method. - */ - init: function(grid) { - this.gridListeners = grid.on({ - beforeselectionextend: this.onBeforeSelectionExtend, - scope: this, - destroyable: true - }); - }, - onBeforeSelectionExtend: function(ownerGrid, sel, extension) { - var columns = this.columns = []; - sel.eachColumn(function(column) { - columns.push(column); - }); - return this.replicateSelection(ownerGrid, sel, extension); - }, - /** - * This is the method which is called when the {@link Ext.grid.selection.SpreadsheetModel spreadsheet} selection model's - * extender handle is dragged and released. - * - * It is passed contextual information about the selection and the extension area. - * - * Subclass authors may override it to gain access to the event and perform their own data replication. - * - * By default, the selection is extended to encompass the selection area. Returning `false` from this method - * vetoes that. - * - * @param {Ext.panel.Table} ownerGrid The owning grid. - * @param {Ext.grid.selection.Selection} sel An object describing the contiguous selected area. - * @param {Object} extension An object describing the type and size of extension. - * @param {String} extension.type `"rows"` or `"columns"` - * @param {Ext.grid.CellContext} extension.start The start (top left) cell of the extension area. - * @param {Ext.grid.CellContext} extension.end The end (bottom right) cell of the extension area. - * @param {number} [extension.columns] The number of columns extended (-ve means on the left side). - * @param {number} [extension.rows] The number of rows extended (-ve means on the top side). - */ - replicateSelection: function(ownerGrid, sel, extension) { - // This can only handle extending rows - if (extension.columns || sel.isColumns) { - return; - } - var me = this, - columns = me.columns, - colCount, j, column, values, startIdx, endIdx, i, increment, store, record, prevValues, prevValue, - selFirstRowIdx = sel.getFirstRowIndex(), - selLastRowIdx = sel.getLastRowIndex(), - selectedRowCount = selLastRowIdx - selFirstRowIdx + 1, - lastTwoRecords = [], - x, y; - colCount = columns.length , store = columns[0].getView().dataSource; - // Single row, just duplicate values into extension - if (selectedRowCount === 1) { - values = me.getColumnValues(sel.view.dataSource.getAt(selFirstRowIdx)); - } else // Multiple rows, take the numeric values from the closest two rows, calculate an array of differences and propagate it - { - values = new Array(colCount); - if (extension.rows < 0) { - lastTwoRecords = [ - store.getAt(selFirstRowIdx + 1), - store.getAt(selFirstRowIdx) - ]; - } else { - lastTwoRecords = [ - store.getAt(selLastRowIdx - 1), - store.getAt(selLastRowIdx) - ]; - } - lastTwoRecords[0] = me.getColumnValues(lastTwoRecords[0]); - lastTwoRecords[1] = me.getColumnValues(lastTwoRecords[1]); - // The values array will be the differences between all numeric columns in the selection of the - // closet two records. - for (j = 0; j < colCount; j++) { - x = lastTwoRecords[1][j]; - y = lastTwoRecords[0][j]; - if (!isNaN(x) && !isNaN(y)) { - values[j] = Number(x) - Number(y); - } - } - } - // Loop from end to start of extension area - if (extension.rows < 0) { - startIdx = extension.end.rowIdx; - endIdx = extension.start.rowIdx - 1; - increment = -1; - } else { - startIdx = extension.start.rowIdx; - endIdx = extension.end.rowIdx + 1; - increment = 1; - } - // Replicate single selected row - if (selectedRowCount === 1) { - for (i = startIdx; i !== endIdx; i += increment) { - record = store.getAt(i); - for (j = 0; j < colCount; j++) { - column = columns[j]; - if (column.dataIndex) { - record.set(column.dataIndex, values[j]); - } - } - } - } else // Add differences from closest two rows - { - for (i = startIdx; i !== endIdx; i += increment) { - record = store.getAt(i); - prevValues = me.getColumnValues(store.getAt(i - increment)); - for (j = 0; j < colCount; j++) { - column = columns[j]; - if (column.dataIndex) { - prevValue = prevValues[j]; - if (!isNaN(prevValue)) { - record.set(column.dataIndex, Ext.coerce(Number(prevValue) + values[j], prevValue)); - } - } - } - } - } - }, - /** - * A utility method, which, when passed a record, uses the {@link #columns} property to extract the values - * of that record which are encompassed by the selection. - * - * Note that columns with no {@link Ext.grid.column.Column#dataIndex dataIndex} cannot yield a value. - * @param {Ext.data.Model} record The record from which to read values. - * @return {Mixed[]} The values of the fields used by the selected column range for the passed record. - */ - getColumnValues: function(record) { - var columns = this.columns, - len = columns.length, - i, column, - result = new Array(columns.length); - for (i = 0; i < len; i++) { - column = columns[i]; - // If there's a dataIndex, get the value - if (column.dataIndex) { - result[i] = record.get(column.dataIndex); - } - } - return result; - }, - destroy: function() { - this.gridListeners = Ext.destroy(this.gridListeners); - this.callParent(); - } -}); - -/** - * A class which encapsulates a range of rows defining a selection in a grid. - * @since 5.1.0 - */ -Ext.define('Ext.grid.selection.Rows', { - extend: 'Ext.grid.selection.Selection', - requires: [ - 'Ext.util.Collection' - ], - type: 'rows', - /** - * @property {Boolean} isRows - * This property indicates the this selection represents selected rows. - * @readonly - */ - isRows: true, - //------------------------------------------------------------------------- - // Base Selection API - clone: function() { - var me = this, - result = new me.self(me.view); - // Clone our record collection - if (me.selectedRecords) { - result.selectedRecords = me.selectedRecords.clone(); - } - // Clone the current drag range - if (me.rangeStart) { - result.setRangeStart(me.rangeStart); - result.setRangeEnd(me.rangeEnd); - } - return result; - }, - //------------------------------------------------------------------------- - // Methods unique to this type of Selection - add: function(record) { - if (!(record.isModel)) { - Ext.raise('Row selection must be passed a record'); - } - var selection = this.selectedRecords || (this.selectedRecords = this.createRecordCollection()); - if (!selection.byInternalId.get(record.internalId)) { - selection.add(record); - this.view.onRowSelect(record); - } - }, - remove: function(record) { - if (!(record.isModel)) { - Ext.raise('Row selection must be passed a record'); - } - var me = this; - if (me.selectedRecords && me.selectedRecords.byInternalId.get(record.internalId)) { - me.selectedRecords.remove(record); - me.view.onRowDeselect(record); - // Flag when selectAll called. - // While this is set, a call to contains will add the record to the collection and return true - me.allSelected = false; - return true; - } - }, - /** - * Returns `true` if the passed {@link Ext.data.Model record} is selected. - * @param {Ext.data.Model} record The record to test. - * @return {Boolean} `true` if the passed {@link Ext.data.Model record} is selected. - */ - contains: function(record) { - if (!record || !record.isModel) { - return false; - } - var me = this, - result = false, - selectedRecords = me.selectedRecords, - recIndex, dragRange; - // Flag set when selectAll is called in th selModel. - // This allows buffered stores to treat all *rendered* records - // as selected, so that the selection model will always encompass - // What the user *sees* as selected - if (me.allSelected) { - me.add(record); - return true; - } - // First check if the record is in our collection - if (selectedRecords) { - result = !!selectedRecords.byInternalId.get(record.internalId); - } - // If not, check if it is within our drag range if we are in the middle of a drag select - if (!result && me.rangeStart != null) { - dragRange = me.getRange(); - recIndex = me.view.dataSource.indexOf(record); - result = recIndex >= dragRange[0] && recIndex <= dragRange[1]; - } - return result; - }, - /** - * Returns the number of records selected - * @return {Number} The number of records selected. - */ - getCount: function() { - var me = this, - selectedRecords = me.selectedRecords, - result = selectedRecords ? selectedRecords.getCount() : 0, - range = me.getRange(), - i, - store = me.view.dataSource; - // If dragging, add all records in the drag that are *not* in the collection - for (i = range[0]; i <= range[1]; i++) { - if (!selectedRecords || !selectedRecords.byInternalId.get(store.getAt(i).internalId)) { - result++; - } - } - return result; - }, - /** - * Returns the records selected. - * @return {Ext.data.Model[]} The records selected. - */ - getRecords: function() { - var selectedRecords = this.selectedRecords; - return selectedRecords ? selectedRecords.getRange() : []; - }, - selectAll: function() { - var me = this; - me.clear(); - me.setRangeStart(0); - me.setRangeEnd(me.view.dataSource.getCount() - 1); - // Adds the records to the collection - me.addRange(); - // While this is set, a call to contains will add the record to the collection and return true. - // This is so that buffer rendered stores can utulize row based selectAll - me.allSelected = true; - }, - /** - * @return {Number} The row index of the first row in the range or zero if no range. - */ - getFirstRowIndex: function() { - return this.getCount() ? this.view.dataSource.indexOf(this.selectedRecords.first()) : 0; - }, - /** - * @return {Number} The row index of the last row in the range or -1 if no range. - */ - getLastRowIndex: function() { - return this.getCount() ? this.view.dataSource.indexOf(this.selectedRecords.first()) : -1; - }, - eachRow: function(fn, scope) { - var selectedRecords = this.selectedRecords; - if (selectedRecords) { - selectedRecords.each(fn, scope || this); - } - }, - eachColumn: function(fn, scope) { - var columns = this.view.getVisibleColumnManager().getColumns(), - len = columns.length, - i; - // If we have any records selected, then all visible columns are selected. - if (this.selectedRecords) { - for (i = 0; i < len; i++) { - if (fn.call(this || scope, columns[i], i) === false) { - return; - } - } - } - }, - eachCell: function(fn, scope) { - var me = this, - selection = me.selectedRecords, - view = me.view, - columns = view.ownerGrid.getVisibleColumnManager().getColumns(), - colCount, i, j, context, range, recCount, - abort = false; - if (columns) { - colCount = columns.length; - context = new Ext.grid.CellContext(view); - // Use Collection#each instead of copying the entire dataset into an array and iterating that. - if (selection) { - selection.each(function(record) { - context.setRow(record); - for (i = 0; i < colCount; i++) { - context.setColumn(columns[i]); - if (fn.call(scope || me, context, context.colIdx, context.rowIdx) === false) { - abort = true; - return false; - } - } - }); - } - // If called during a drag select, or SHIFT+arrow select, include the drag range - if (!abort && me.rangeStart != null) { - range = me.getRange(); - me.view.dataSource.getRange(range[0], range[1], { - callback: function(records) { - recCount = records.length; - for (i = 0; !abort && i < recCount; i++) { - context.setRow(records[i]); - for (j = 0; !abort && j < colCount; j++) { - context.setColumn(columns[j]); - if (fn.call(scope || me, context, context.colIdx, context.rowIdx) === false) { - abort = true; - } - } - } - } - }); - } - } - }, - /** - * This method is called to indicate the start of multiple changes to the selected row set. - * - * Internally this method increments a counter that is decremented by `{@link #endUpdate}`. It - * is important, therefore, that if you call `beginUpdate` directly you match that - * call with a call to `endUpdate` or you will prevent the collection from updating - * properly. - */ - beginUpdate: function() { - var selectedRecords = this.selectedRecords; - if (selectedRecords) { - selectedRecords.beginUpdate(); - } - }, - /** - * This method is called after modifications are complete on a selected row set. For details - * see `{@link #beginUpdate}`. - */ - endUpdate: function() { - var selectedRecords = this.selectedRecords; - if (selectedRecords) { - selectedRecords.endUpdate(); - } - }, - destroy: function() { - this.selectedRecords = Ext.destroy(this.selectedRecords); - this.callParent(); - }, - //------------------------------------------------------------------------- - privates: { - /** - * @private - */ - clear: function() { - var me = this, - view = me.view; - // Flag when selectAll called. - // While this is set, a call to contains will add the record to the collection and return true - me.allSelected = false; - if (me.selectedRecords) { - me.eachRow(function(record) { - view.onRowDeselect(record); - }); - me.selectedRecords.clear(); - } - }, - /** - * @return {Boolean} - * @private - */ - isAllSelected: function() { - // This branch has a flag because it encompasses a possibly buffered store, - // where the full dataset might not be present, so a flag indicates that all - // records are selected even as they flow into or out of the buffered page cache. - return !!this.allSelected; - }, - /** - * Used during drag/shift+downarrow range selection on start. - * @param {Number} start The start row index of the row drag selection. - * @private - */ - setRangeStart: function(start) { - // Flag when selectAll called. - // While this is set, a call to contains will add the record to the collection and return true - this.allSelected = false; - this.rangeStart = this.rangeEnd = start; - this.view.onRowSelect(start); - }, - /** - * Used during drag/shift+downarrow range selection on change of row. - * @param {Number} end The end row index of the row drag selection. - * @private - */ - setRangeEnd: function(end) { - var me = this, - range, lastRange, rowIdx, row, - view = me.view, - store = view.dataSource, - rows = view.all, - selected = me.selectedRecords, - rec; - // Update the range as requested, then calculate the - // range in lowest index first form - me.rangeEnd = end; - range = me.getRange(); - lastRange = me.lastRange || range; - // Loop through the union of last range and current range - for (rowIdx = Math.max(Math.min(range[0], lastRange[0]), rows.startIndex) , end = Math.min(Math.max(range[1], lastRange[1]), rows.endIndex); rowIdx <= end; rowIdx++) { - row = rows.item(rowIdx); - // If we are outside the current range, deselect - if (rowIdx < range[0] || rowIdx > range[1]) { - // If we are deselecting, also remove from collection - if (selected && (rec = selected.byInternalId.get(store.getAt(rowIdx).internalId))) { - selected.remove(rec); - } - view.onRowDeselect(rowIdx); - } else { - view.onRowSelect(rowIdx); - } - } - me.lastRange = range; - }, - extendRange: function(extensionVector) { - var me = this, - store = me.view.dataSource, - i; - for (i = extensionVector.start.rowIdx; i <= extensionVector.end.rowIdx; i++) { - me.add(store.getAt(i)); - } - }, - /** - * @return {Number[]} - * @private - */ - getRange: function() { - var start = this.rangeStart, - end = this.rangeEnd; - if (start == null) { - return [ - 0, - -1 - ]; - } else if (start <= end) { - return [ - start, - end - ]; - } - return [ - end, - start - ]; - }, - /** - * Returns the size of the mousedown+drag, or SHIFT+arrow selection range. - * @return {Number} - * @private - */ - getRangeSize: function() { - var range = this.getRange(); - return range[1] - range[0] + 1; - }, - /** - * @return {Ext.util.Collection} - * @private - */ - createRecordCollection: function() { - var store = this.view.dataSource, - result = new Ext.util.Collection({ - rootProperty: 'data', - extraKeys: { - byInternalId: { - rootProperty: false, - property: 'internalId' - } - }, - sorters: [ - function(r1, r2) { - return store.indexOf(r1) - store.indexOf(r2); - } - ] - }); - return result; - }, - /** - * Called at the end of a drag, or shift+downarrow row range select. - * The record range delineated by the start and end row indices is added to the selected Collection. - * @private - */ - addRange: function() { - var me = this, - range, selection; - if (me.rangeStart != null) { - range = me.getRange(); - selection = me.selectedRecords || (me.selectedRecords = me.createRecordCollection()); - me.view.dataSource.getRange(range[0], range[1], { - callback: function(range) { - selection.add.apply(selection, range); - } - }); - // Clear the drag range - me.setRangeStart(me.lastRange = null); - } - }, - onSelectionFinish: function() { - var me = this, - range = me.getContiguousSelection(); - if (range) { - me.view.getSelectionModel().onSelectionFinish(me, new Ext.grid.CellContext(me.view).setPosition(range[0], 0), new Ext.grid.CellContext(me.view).setPosition(range[1], me.view.getVisibleColumnManager().getColumns().length - 1)); - } else { - me.view.getSelectionModel().onSelectionFinish(me); - } - }, - /** - * @return {Array} `[startRowIndex, endRowIndex]` if the selection represents a visually contiguous set of rows. - * The SelectionReplicator is only enabled if there is a contiguous block. - * @private - */ - getContiguousSelection: function() { - var store = this.view.dataSource, - selection, len, i; - if (this.selectedRecords) { - selection = Ext.Array.sort(this.selectedRecords.getRange(), function(r1, r2) { - return store.indexOf(r1) - store.indexOf(r2); - }); - len = selection.length; - if (len) { - for (i = 1; i < len; i++) { - if (store.indexOf(selection[i]) !== store.indexOf(selection[i - 1]) + 1) { - return false; - } - } - return [ - store.indexOf(selection[0]), - store.indexOf(selection[len - 1]) - ]; - } - } - } - } -}); - -Ext.define('Ext.grid.selection.SelectionExtender', { - extend: 'Ext.dd.DragTracker', - maskBox: {}, - constructor: function(config) { - var me = this; - // We can only initialize properly if there are elements to work with - if (config.view.rendered) { - me.initSelectionExtender(config); - } else { - me.view = config.view; - config.view.on({ - render: me.initSelectionExtender, - args: [ - config - ], - scope: me - }); - } - }, - initSelectionExtender: function(config) { - var me = this, - displayMode = Ext.dom.Element.DISPLAY; - me.el = config.view.el; - me.handle = config.view.ownerGrid.body.createChild({ - cls: Ext.baseCSSPrefix + 'ssm-extender-drag-handle', - style: 'display:none' - }).setVisibilityMode(displayMode); - me.handle.on({ - contextmenu: function(e) { - e.stopEvent(); - } - }); - me.mask = me.el.createChild({ - cls: Ext.baseCSSPrefix + 'ssm-extender-mask', - style: 'display:none' - }).setVisibilityMode(displayMode); - me.superclass.constructor.call(me, config); - // Mask and andle must survive being orphaned - me.mask.skipGarbageCollection = me.handle.skipGarbageCollection = true; - me.viewListeners = me.view.on({ - scroll: me.onViewScroll, - scope: me, - destroyable: true - }); - me.gridListeners = me.view.ownerGrid.on({ - columnResize: me.alignHandle, - scope: me, - destroyable: true - }); - me.extendX = !!(me.axes & 1); - me.extendY = !!(me.axes & 2); - }, - setHandle: function(firstPos, lastPos) { - var me = this; - if (!me.view.rendered) { - me.view.on({ - render: me.initSelectionExtender, - args: [ - firstPos, - lastPos - ], - scope: me - }); - return; - } - me.firstPos = firstPos; - me.lastPos = lastPos; - if (firstPos && lastPos) { - if (me.curPos) { - me.curPos.setPosition(lastPos); - } else { - me.curPos = lastPos.clone(); - } - // If we've done a "select all rows" and there is buffered rendering, then - // the cells might not be rendered, so we can't activate the replicator. - if (firstPos && lastPos) { - // Align centre of handle with bottom-right corner of last cell if possible. - me.alignHandle(); - } - } else { - me.disable(); - } - }, - alignHandle: function() { - var me = this, - lastCell = me.lastPos && me.lastPos.getCell(); - // Cell corresponding to the position might not be rendered. - // This will be called upon scroll - if (lastCell) { - me.enable(); - me.handle.alignTo(lastCell, 'c-br'); - } else { - me.disable(); - } - }, - enable: function() { - this.handle.show(); - this.callParent(); - }, - disable: function() { - this.handle.hide(); - this.mask.hide(); - this.callParent(); - }, - onDrag: function(e) { - // pointer-events-none is not supported on IE10m. - // So if shrinking the extension zone, the mousemove target may be the mask. - // We have to retarget on the cell *below* that. - if (e.target === this.mask.dom) { - this.mask.hide(); - e.target = document.elementFromPoint.apply(document, e.getXY()); - this.mask.show(); - } - var me = this, - view = me.view, - viewTop = view.el.getY(), - viewLeft = view.el.getX(), - overCell = e.getTarget(me.view.getCellSelector()), - scrollTask = me.scrollTask || (me.scrollTask = Ext.util.TaskManager.newTask({ - run: me.doAutoScroll, - scope: me, - interval: 10 - })), - scrollBy = me.scrollBy || (me.scrollBy = []); - // Dragged outside the view; stop scrolling. - if (!me.el.contains(e.target)) { - scrollBy[0] = scrollBy[1] = 0; - return scrollTask.stop(); - } - // Neart bottom of view - if (me.lastXY[1] > viewTop + view.el.getHeight(true) - 15) { - if (me.extendY) { - scrollBy[1] = 3; - scrollTask.start(); - } - } - // Near top of view - else if (me.lastXY[1] < viewTop + 10) { - if (me.extendY) { - scrollBy[1] = -3; - scrollTask.start(); - } - } - // Near right edge of view - else if (me.lastXY[0] > viewLeft + view.el.getWidth(true) - 15) { - if (me.extendX) { - scrollBy[0] = 3; - scrollTask.start(); - } - } - // Near left edge of view - else if (me.lastXY[0] < viewLeft + 10) { - if (me.extendX) { - scrollBy[0] = -3; - scrollTask.start(); - } - } else // Not near an edge, cancel autoscrolling - { - scrollBy[0] = scrollBy[1] = 0; - scrollTask.stop(); - } - if (overCell && overCell !== me.lastOverCell) { - me.lastOverCell = overCell; - me.syncMaskOnCell(overCell); - } - }, - doAutoScroll: function() { - var me = this, - view = me.view, - scrollOverCell; - // Bump the view in whatever direction was decided in the onDrag method. - view.scrollBy.apply(view, me.scrollBy); - // Mouseover does not fire on autoscroll so see where the mouse is over on each scroll - scrollOverCell = document.elementFromPoint.apply(document, me.lastXY); - if (scrollOverCell) { - scrollOverCell = Ext.fly(scrollOverCell).up(view.cellSelector); - if (scrollOverCell && scrollOverCell !== me.lastOverCell) { - me.lastOverCell = scrollOverCell; - me.syncMaskOnCell(scrollOverCell); - } - } - }, - onEnd: function(e) { - var me = this; - if (me.scrollTask) { - me.scrollTask.stop(); - } - if (me.extensionDescriptor) { - me.disable(); - me.view.getSelectionModel().extendSelection(me.extensionDescriptor); - } - }, - onViewScroll: function() { - var me = this; - // If being dragged - if (me.active && me.lastOverCell) { - me.syncMaskOnCell(me.lastOverCell); - } - // We have been applied to a selection block - if (me.firstPos) { - // Align centre of handle with bottom-right corner of last cell if possible. - me.alignHandle(); - } - }, - syncMaskOnCell: function(overCell) { - var me = this, - view = me.view, - rows = view.all, - curPos = me.curPos, - maskBox = me.maskBox, - selRegion, - firstPos = me.firstPos.clone(), - lastPos = me.lastPos.clone(), - extensionStart = me.firstPos.clone(), - extensionEnd = me.lastPos.clone(); - // Constrain cell positions to be within rendered range. - firstPos.setRow(Math.min(Math.max(firstPos.rowIdx, rows.startIndex), rows.endIndex)); - lastPos.setRow(Math.min(Math.max(lastPos.rowIdx, rows.startIndex), rows.endIndex)); - me.selectionRegion = selRegion = firstPos.getCell().getRegion().union(lastPos.getCell().getRegion()); - curPos.setPosition(view.getRecord(overCell), view.getHeaderByCell(overCell)); - // The above calls require the cell to be a DOM reference - overCell = Ext.fly(overCell); - // Reset border to default, which is the overall border setting from SASS - // We disable the border which is contiguous to the selection. - me.mask.dom.style.borderTopWidth = me.mask.dom.style.borderRightWidth = me.mask.dom.style.borderBottomWidth = me.mask.dom.style.borderLeftWidth = ''; - // Dragged above the selection - if (curPos.rowIdx < me.firstPos.rowIdx && me.extendY) { - me.extensionDescriptor = { - type: 'rows', - start: extensionStart.setRow(curPos.rowIdx), - end: extensionEnd.setRow(me.firstPos.rowIdx - 1), - rows: curPos.rowIdx - me.firstPos.rowIdx, - mousePosition: me.lastXY - }; - me.mask.dom.style.borderBottomWidth = '0'; - maskBox.x = selRegion.x; - maskBox.y = overCell.getY(); - maskBox.width = selRegion.right - selRegion.left; - maskBox.height = selRegion.top - overCell.getY(); - } - // Dragged below selection - else if (curPos.rowIdx > me.lastPos.rowIdx && me.extendY) { - me.extensionDescriptor = { - type: 'rows', - start: extensionStart.setRow(me.lastPos.rowIdx + 1), - end: extensionEnd.setRow(curPos.rowIdx), - rows: curPos.rowIdx - me.lastPos.rowIdx, - mousePosition: me.lastXY - }; - me.mask.dom.style.borderTopWidth = '0'; - maskBox.x = selRegion.x; - maskBox.y = selRegion.bottom; - maskBox.width = selRegion.right - selRegion.left; - maskBox.height = overCell.getRegion().bottom - selRegion.bottom; - } else // row position is within selected row range - { - // Dragged to left of selection - if (curPos.colIdx < me.firstPos.colIdx && me.extendX) { - me.extensionDescriptor = { - type: 'columns', - start: extensionStart.setColumn(curPos.colIdx), - end: extensionEnd.setColumn(me.firstPos.colIdx - 1), - columns: curPos.colIdx - me.firstPos.colIdx, - mousePosition: me.lastXY - }; - me.mask.dom.style.borderRightWidth = '0'; - maskBox.x = overCell.getX(); - maskBox.y = selRegion.top; - maskBox.width = selRegion.left - overCell.getX(); - maskBox.height = selRegion.bottom - selRegion.top; - } - // Dragged to right of selection - else if (curPos.colIdx > me.lastPos.colIdx && me.extendX) { - me.extensionDescriptor = { - type: 'columns', - start: extensionStart.setColumn(me.lastPos.colIdx + 1), - end: extensionEnd.setColumn(curPos.colIdx), - columns: curPos.colIdx - me.lastPos.colIdx, - mousePosition: me.lastXY - }; - me.mask.dom.style.borderLeftWidth = '0'; - maskBox.x = selRegion.right; - maskBox.y = selRegion.top; - maskBox.width = overCell.getRegion().right - selRegion.right; - maskBox.height = selRegion.bottom - selRegion.top; - } else { - me.extensionDescriptor = null; - } - } - if (view.ownerGrid.hasListeners.selectionextenderdrag) { - view.ownerGrid.fireEvent('selectionextenderdrag', view.ownerGrid, view.getSelectionModel().getSelected(), me.extensionDescriptor); - } - if (me.extensionDescriptor) { - me.mask.show(); - me.mask.setBox(maskBox); - } else { - me.mask.hide(); - } - }, - destroy: function() { - var me = this; - Ext.destroy(me.gridListeners, me.viewListeners, me.mask, me.handle); - me.callParent(); - } -}); - -/** - * A selection model for {@link Ext.grid.Panel grids} which allows you to select data in - * a spreadsheet-like manner. - * - * Supported features: - * - * - Single / Range / Multiple individual row selection. - * - Single / Range cell selection. - * - Column selection by click selecting column headers. - * - Select / deselect all by clicking in the top-left, header. - * - Adds row number column to enable row selection. - * - Optionally you can enable row selection using checkboxes - * - * # Example usage - * - * @example - * var store = Ext.create('Ext.data.Store', { - * fields: ['name', 'email', 'phone'], - * data: [ - * { name: 'Lisa', email: 'lisa@simpsons.com', phone: '555-111-1224' }, - * { name: 'Bart', email: 'bart@simpsons.com', phone: '555-222-1234' }, - * { name: 'Homer', email: 'homer@simpsons.com', phone: '555-222-1244' }, - * { name: 'Marge', email: 'marge@simpsons.com', phone: '555-222-1254' } - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Simpsons', - * store: store, - * width: 400, - * renderTo: Ext.getBody(), - * columns: [ - * { text: 'Name', dataIndex: 'name' }, - * { text: 'Email', dataIndex: 'email', flex: 1 }, - * { text: 'Phone', dataIndex: 'phone' } - * ], - * selModel: { - * type: 'spreadsheet' - * } - * }); - * - * @since 5.1.0 - */ -Ext.define('Ext.grid.selection.SpreadsheetModel', { - extend: 'Ext.selection.Model', - requires: [ - 'Ext.grid.selection.Selection', - 'Ext.grid.selection.Cells', - 'Ext.grid.selection.Rows', - 'Ext.grid.selection.Columns', - 'Ext.grid.selection.SelectionExtender' - ], - // TODO: cmd-auto-dependency - alias: 'selection.spreadsheet', - isSpreadsheetModel: true, - config: { - /** - * @cfg {Boolean} [columnSelect=false] - * Set to `true` to enable selection of columns. - * - * **NOTE**: This will remove sorting on header click and instead provide column - * selection and deselection. Sorting is still available via column header menu. - */ - columnSelect: { - $value: false, - lazy: true - }, - /** - * @cfg {Boolean} [cellSelect=true] - * Set to `true` to enable selection of individual cells or a single rectangular - * range of cells. This will provide cell range selection using click, and - * potentially drag to select a rectangular range. You can also use "SHIFT + arrow" - * key navigation to select a range of cells. - */ - cellSelect: { - $value: true, - lazy: true - }, - /** - * @cfg {Boolean} [rowSelect=true] - * Set to `true` to enable selection of rows by clicking on a row number column. - * - * *Note*: This feature will add the row number as the first column. - */ - rowSelect: { - $value: true, - lazy: true - }, - /** - * @cfg {Boolean} [dragSelect=true] - * Set to `true` to enables cell range selection by cell dragging. - */ - dragSelect: { - $value: true, - lazy: true - }, - /** - * @cfg {Ext.grid.selection.Selection} [selected] - * Pass an instance of one of the subclasses of {@link Ext.grid.selection.Selection}. - */ - selected: null, - /** - * @cfg {String} extensible - * This configures whether this selection model is to implement a mouse based dragging gesture to extend a *contiguou*s selection. - * - * Note that if there are multiple, discontiguous selected rows or columns, selection extension is not available. - * - * If set, then the bottom right corner of the contiguous selection will display a drag handle. By dragging this, an extension area - * may be defined into which the selection is extended. - * - * Upon the end of the drag, the {@link Ext.panel.Table#beforeselectionextend beforeselectionextend} event will be fired though the - * encapsulating grid. Event handlers may manipulate the store data in any way. - * - * Possible values for this configuration are - * - * - `"x"` Only allow extending the block to the left or right. - * - `"y"` Only allow extending the block above or below. - * - `"xy"` Allow extebnding the block in both dimensions. - * - `"both"` Allow extebnding the block in both dimensions. - * - `true` Allow extebnding the block in both dimensions. - */ - extensible: { - $value: true, - lazy: true - } - }, - /** - * @event selectionchange - * Fired *by the grid* after the selection changes. Return `false` to veto the selection extension. - * @param {Ext.grid.Panel} grid The grid whose selection has changed. - * @param {Ext.grid.selection.Selection} selection A subclass of - * {@link Ext.grid.selection.Selection} describing the new selection. - */ - /** - * @cfg {Boolean} checkboxSelect [checkboxSelect=false] - * Enables selection of the row via clicking on checkbox. Note: this feature will add - * new column at position specified by {@link #checkboxColumnIndex}. - */ - checkboxSelect: false, - /** - * @cfg {Number/String} [checkboxColumnIndex=0] - * The index at which to insert the checkbox column. - * Supported values are a numeric index, and the strings 'first' and 'last'. Only valid when set - * *before* render. - */ - checkboxColumnIndex: 0, - /** - * @cfg {Boolean} [showHeaderCheckbox=true] - * Configure as `false` to not display the header checkbox at the top of the checkbox column - * when {@link #checkboxSelect} is set. - */ - showHeaderCheckbox: true, - /** - * @cfg {Number/String} [checkboxHeaderWidth=24] - * Width of checkbox column. - */ - checkboxHeaderWidth: 24, - /** - * @cfg {Number/String} [rowNumbererHeaderWidth=46] - * Width of row numbering column. - */ - rowNumbererHeaderWidth: 46, - columnSelectCls: Ext.baseCSSPrefix + 'ssm-column-select', - rowNumbererHeaderCls: Ext.baseCSSPrefix + 'ssm-row-numberer-hd', - /** - * @private - */ - checkerOnCls: Ext.baseCSSPrefix + 'grid-hd-checker-on', - tdCls: Ext.baseCSSPrefix + 'grid-cell-special ' + Ext.baseCSSPrefix + 'grid-cell-row-checker', - /** - * @method getCount - * This method is not supported by SpreadsheetModel. - * - * To interrogate the selection use {@link #getSelected} which will return an instance of one - * of the three selection types, or `null` if no selection. - * - * The three selection types are: - * - * * {@link Ext.grid.selection.Rows} - * * {@link Ext.grid.selection.Columns} - * * {@link Ext.grid.selection.Cells} - */ - /** - * @method getSelectionMode - * This method is not supported by SpreadsheetModel. - */ - /** - * @method setSelectionMode - * This method is not supported by SpreadsheetModel. - */ - /** - * @method setLocked - * This method is not currently supported by SpreadsheetModel. - */ - /** - * @method isLocked - * This method is not currently supported by SpreadsheetModel. - */ - /** - * @method isRangeSelected - * This method is not supported by SpreadsheetModel. - * - * To interrogate the selection use {@link #getSelected} which will return an instance of one - * of the three selection types, or `null` if no selection. - * - * The three selection types are: - * - * * {@link Ext.grid.selection.Rows} - * * {@link Ext.grid.selection.Columns} - * * {@link Ext.grid.selection.Cells} - */ - /** - * @member Ext.panel.Table. - * @event beforeselectionextend An event fired when an extension block is extended - * using a drag gesture. Only fired when the SpreadsheetSelectionModel is used and - * configured with the - * {@link Ext.grid.selection.SpreadsheetModel#extensible extensible} config. - * @param {Ext.panel.Table} grid The owning grid. - * @param {Ext.grid.selection.Selection} An object which encapsulates a contiguous selection block. - * @param {Object} extension An object describing the type and size of extension. - * @param {String} extension.type `"rows"` or `"columns"` - * @param {Ext.grid.CellContext} extension.start The start (top left) cell of the extension area. - * @param {Ext.grid.CellContext} extension.end The end (bottom right) cell of the extension area. - * @param {number} [extension.columns] The number of columns extended (-ve means on the left side). - * @param {number} [extension.rows] The number of rows extended (-ve means on the top side). - */ - /** - * @member Ext.panel.Table. - * @event selectionextenderdrag An event fired when an extension block is dragged to - * encompass a new range. Only fired when the SpreadsheetSelectionModel is used and - * configured with the - * {@link Ext.grid.selection.SpreadsheetModel#extensible extensible} config. - * @param {Ext.panel.Table} grid The owning grid. - * @param {Ext.grid.selection.Selection} An object which encapsulates a contiguous selection block. - * @param {Object} extension An object describing the type and size of extension. - * @param {String} extension.type `"rows"` or `"columns"` - * @param {HTMLElement} extension.overCell The grid cell over which the mouse is being dragged. - * @param {Ext.grid.CellContext} extension.start The start (top left) cell of the extension area. - * @param {Ext.grid.CellContext} extension.end The end (bottom right) cell of the extension area. - * @param {number} [extension.columns] The number of columns extended (-ve means on the left side). - * @param {number} [extension.rows] The number of rows extended (-ve means on the top side). - */ - /** - * @private - */ - bindComponent: function(view) { - var me = this, - viewListeners, lockedGrid; - if (me.view !== view) { - if (me.view) { - me.navigationModel = null; - Ext.destroy(me.viewListeners, me.navigationListeners); - } - me.view = view; - if (view) { - // We need to realize our lazy configs now that we have the view... - me.getCellSelect(); - lockedGrid = view.ownerGrid.lockedGrid; - // If there is a locked grid, process it now - if (lockedGrid) { - me.hasLockedHeader = true; - me.onViewCreated(lockedGrid, lockedGrid.getView()); - } else // Otherwise, get back to us when the view is fully created so that we can tweak its headerCt - { - view.grid.on({ - viewcreated: me.onViewCreated, - scope: me, - single: true - }); - } - me.gridListeners = view.ownerGrid.on({ - columnschanged: me.onColumnsChanged, - columnmove: me.onColumnMove, - scope: me, - destroyable: true - }); - viewListeners = me.getViewListeners(); - viewListeners.scope = me; - viewListeners.destroyable = true; - me.viewListeners = view.on(viewListeners); - me.navigationModel = view.getNavigationModel(); - me.navigationListeners = me.navigationModel.on({ - navigate: me.onNavigate, - scope: me, - destroyable: true - }); - // Add class to add special cursor pointer to column headers - if (me.getColumnSelect()) { - view.ownerGrid.addCls(me.columnSelectCls); - } - } - } - }, - /** - * Retrieve a configuration to be used in a HeaderContainer. - * This should be used when checkboxSelect is set to false. - * @protected - */ - getCheckboxHeaderConfig: function() { - var me = this, - showCheck = me.showHeaderCheckbox !== false; - return { - ignoreExport: true, - isCheckerHd: showCheck, - text: ' ', - clickTargetName: 'el', - width: me.checkboxHeaderWidth, - sortable: false, - draggable: false, - resizable: false, - hideable: false, - menuDisabled: true, - dataIndex: '', - tdCls: me.tdCls, - cls: showCheck ? Ext.baseCSSPrefix + 'column-header-checkbox ' : '', - defaultRenderer: me.checkboxRenderer.bind(me), - editRenderer: ' ', - locked: me.hasLockedHeader - }; - }, - /** - * Generates the HTML to be rendered in the injected checkbox column for each row. - * Creates the standard checkbox markup by default; can be overridden to provide custom rendering. - * See {@link Ext.grid.column.Column#renderer} for description of allowed parameters. - * @private - */ - checkboxRenderer: function() { - return ''; - }, - /** - * @private - */ - onHeaderClick: function(headerCt, header, e) { - // Template method. See base class - var me = this, - sel = me.selected, - cm, range, i; - if (header === me.numbererColumn || header === me.checkColumn) { - e.stopEvent(); - // Not all selected, select all - if (!sel || !sel.isAllSelected()) { - me.selectAll(); - } else { - me.deselectAll(); - } - me.updateHeaderState(); - me.lastColumnSelected = null; - } else if (me.columnSelect) { - if (e.shiftKey && sel && sel.lastColumnSelected) { - sel.clear(); - cm = this.view.ownerGrid.getVisibleColumnManager(); - range = Ext.Array.sort([ - cm.indexOf(sel.lastColumnSelected), - cm.indexOf(header) - ], Ext.Array.numericSortFn); - for (i = range[0]; i <= range[1]; i++) { - me.selectColumn(cm.getHeaderAtIndex(i), true); - } - } else { - if (me.isColumnSelected(header)) { - me.deselectColumn(header); - me.selected.lastColumnSelected = null; - } else { - me.selectColumn(header, e.ctrlKey); - me.selected.lastColumnSelected = header; - } - } - } - }, - /** - * @private - */ - updateHeaderState: function() { - // check to see if all records are selected - var me = this, - store = me.view.dataSource, - storeCount = store.getCount(), - views = me.views, - sel = me.selected, - isChecked = sel && sel.isRows && !store.isBufferedStore && storeCount > 0 && (storeCount === sel.getCount()), - checkHd = me.checkColumn, - cls = me.checkerOnCls; - if (views && views.length) { - if (checkHd) { - if (isChecked) { - checkHd.addCls(cls); - } else { - checkHd.removeCls(cls); - } - } - } - }, - /** - * Handles the grid's beforereconfigure event. Adds the checkbox header if the columns have been reconfigured. - * Also adds the row numberer. - * @param {Ext.panel.Table} grid - * @param {Ext.data.Store} store - * @param {Object[]} columns - * @private - */ - onBeforeReconfigure: function(grid, store, columns, oldStore, oldColumns) { - var me = this; - if (columns) { - Ext.suspendLayouts(); - // Remove our utility columns without destroying. Reconfigure destroys columns by default. - // Add them into the new column set at the beginning. - if (me.numbererColumn) { - me.numbererColumn.ownerCt.remove(me.numbererColumn, false); - columns.unshift(me.numbererColumn); - } - if (me.checkColumn) { - me.checkColumn.ownerCt.remove(me.checkColumn, false); - columns.unshift(me.checkColumn); - } - Ext.resumeLayouts(); - } - }, - /** - * This is a helper method to create a cell context which encapsulates one cell in a grid view. - * - * It will contain the following properties: - * colIdx - column index - * rowIdx - row index - * column - {@link Ext.grid.column.Column Column} under which the cell is located. - * record - {@link Ext.data.Model} Record from which the cell derives its data. - * view - The view. If this selection model is for a locking grid, this will be the - * outermost view, the {@link Ext.grid.locking.View} which encapsulates the sub - * grids. Column indices are relative to the outermost view's visible column set. - * - * @param {Number} record Record for which to select the cell, or row index. - * @param {Number} column Grid column header, or column index. - * @return {Ext.grid.CellContext} A context object describing the cell. Note that the `rowidx` and `colIdx` properties are only valid - * at the time the context object is created. Column movement, sorting or filtering might changed where the cell is. - * @private - */ - getCellContext: function(record, column) { - return new Ext.grid.CellContext(this.view.ownerGrid.getView()).setPosition(record, column); - }, - select: function(records, keepExisting, suppressEvent) { - // API docs are inherited - var me = this, - sel = me.selected, - view = me.view, - store = view.dataSource, - len, i, record, - changed = false; - // Ensure selection object is of the correct type - if (!sel || !sel.isRows || sel.view !== view) { - me.resetSelection(true); - sel = me.selected = new Ext.grid.selection.Rows(view); - } else if (!keepExisting) { - sel.clear(); - } - if (!Ext.isArray(records)) { - records = [ - records - ]; - } - len = records.length; - for (i = 0; i < len; i++) { - record = records[i]; - if (typeof record === 'number') { - record = store.getAt(record); - } - if (!sel.contains(record)) { - sel.add(record); - changed = true; - } - } - if (changed) { - me.updateHeaderState(); - if (suppressEvent) { - me.fireSelectionChange(); - } - } - }, - deselect: function(records, suppressEvent) { - // API docs are inherited - var me = this, - sel = me.selected, - store = me.view.dataSource, - len, i, record, - changed = false; - if (sel && sel.isRows) { - if (!Ext.isArray(records)) { - records = [ - records - ]; - } - len = records.length; - for (i = 0; i < len; i++) { - record = records[i]; - if (typeof record === 'number') { - record = store.getAt(record); - } - changed = changed || sel.remove(record); - } - } - if (changed) { - me.updateHeaderState(); - if (!suppressEvent) { - me.fireSelectionChange(); - } - } - }, - /** - * This method allows programmatic selection of the cell range. - * - * @example - * var store = Ext.create('Ext.data.Store', { - * fields : ['name', 'email', 'phone'], - * data : { - * items : [ - * { name : 'Lisa', email : 'lisa@simpsons.com', phone : '555-111-1224' }, - * { name : 'Bart', email : 'bart@simpsons.com', phone : '555-222-1234' }, - * { name : 'Homer', email : 'homer@simpsons.com', phone : '555-222-1244' }, - * { name : 'Marge', email : 'marge@simpsons.com', phone : '555-222-1254' } - * ] - * }, - * proxy : { - * type : 'memory', - * reader : { - * type : 'json', - * root : 'items' - * } - * } - * }); - * - * var grid = Ext.create('Ext.grid.Panel', { - * title : 'Simpsons', - * store : store, - * width : 400, - * renderTo : Ext.getBody(), - * columns : [ - * columns: [ - * { text: 'Name', dataIndex: 'name' }, - * { text: 'Email', dataIndex: 'email', flex: 1 }, - * { text: 'Phone', dataIndex: 'phone', width:120 }, - * { - * text:'Combined', dataIndex: 'name', width : 300, - * renderer: function(value, metaData, record, rowIndex, colIndex, store, view) { - * console.log(arguments); - * return value + ' has email: ' + record.get('email'); - * } - * } - * ], - * ], - * selType: 'spreadsheet' - * }); - * - * var model = grid.getSelectionModel(); // get selection model - * - * // We will create range of 4 cells. - * - * // Now set the range and prevent rangeselect event from being fired. - * // We can use a simple array when we have no locked columns. - * model.selectCells([0, 0], [1, 1], true); - * - * @param rangeStart {Ext.grid.CellContext/Number[]} Range starting position. Can be either Cell context or a `[rowIndex, columnIndex]` numeric array. - * - * Note that when a numeric array is used in a locking grid, the column indices are relative to the outermost grid, encompassing locked *and* normal sides. - * @param rangeEnd {Ext.grid.CellContext/Number[]} Range end position. Can be either Cell context or a `[rowIndex, columnIndex]` numeric array. - * - * Note that when a numeric array is used in a locking grid, the column indices are relative to the outermost grid, encompassing locked *and* normal sides. - * @param {Boolean} [suppressEvent] Pass `true` to prevent firing the - * `{@link #selectionchange}` event. - */ - selectCells: function(rangeStart, rangeEnd, suppressEvent) { - var me = this, - view = me.view.ownerGrid.view, - sel; - rangeStart = rangeStart.isCellContext ? rangeStart.clone() : new Ext.grid.CellContext(view).setPosition(rangeStart); - rangeEnd = rangeEnd.isCellContext ? rangeEnd.clone() : new Ext.grid.CellContext(view).setPosition(rangeEnd); - me.resetSelection(true); - me.selected = sel = new Ext.grid.selection.Cells(rangeStart.view); - sel.setRangeStart(rangeStart); - sel.setRangeEnd(rangeEnd); - if (!suppressEvent) { - me.fireSelectionChange(); - } - }, - /** - * Select all the data if possible. - * - * If {@link #rowSelect} is `true`, then all *records* will be selected. - * - * If {@link #cellSelect} is `true`, then all *rendered cells* will be selected. - * - * If {@link #columnSelect} is `true`, then all *columns* will be selected. - * - * @param {Boolean} [suppressEvent] Pass `true` to prevent firing the - * `{@link #selectionchange}` event. - */ - selectAll: function(suppressEvent) { - var me = this, - sel = me.selected, - doSelect, - view = me.view; - if (me.rowSelect) { - if (!sel || !sel.isRows) { - me.resetSelection(true); - me.selected = sel = new Ext.grid.selection.Rows(view); - } - doSelect = true; - } else if (me.cellSelect) { - if (!sel || !sel.isCells) { - me.resetSelection(true); - me.selected = sel = new Ext.grid.selection.Cells(view); - } - doSelect = true; - } else if (me.columnSelect) { - if (!sel || !sel.isColumns) { - me.resetSelection(true); - me.selected = sel = new Ext.grid.selection.Columns(view); - } - doSelect = true; - } - if (doSelect) { - sel.selectAll(); - me.updateHeaderState(); - if (!suppressEvent) { - me.fireSelectionChange(); - } - } - }, - /** - * Clears the selection. - * @param {Boolean} [suppressEvent] Pass `true` to prevent firing the - * `{@link #selectionchange}` event. - */ - deselectAll: function(suppressEvent) { - var sel = this.selected; - if (sel && sel.getCount()) { - sel.clear(); - if (!suppressEvent) { - this.fireSelectionChange(); - } - } - }, - /** - * Select one or more rows. - * @param rows {Ext.data.Model[]} Records to select. - * @param {Boolean} [keepSelection=false] Pass `true` to keep previous selection. - * @param {Boolean} [suppressEvent] Pass `true` to prevent firing the - * `{@link #selectionchange}` event. - */ - selectRows: function(rows, keepSelection, suppressEvent) { - var me = this, - sel = me.selected, - isSelectingRows = sel && sel.isRows, - len = rows.length, - i; - if (!keepSelection || !isSelectingRows) { - me.resetSelection(true); - } - if (!isSelectingRows) { - me.selected = sel = new Ext.grid.selection.Rows(me.view); - } - if (rows.isEntity) { - sel.add(rows); - } else { - for (i = 0; i < len; i++) { - sel.add(rows[i]); - }; - } - - if (!suppressEvent) { - me.fireSelectionChange(); - } - }, - isSelected: function(record) { - // API docs are inherited. - return this.isRowSelected(record); - }, - /** - * Selects a column. - * @param {Ext.grid.column.Column} column Column to select. - * @param {Boolean} [keepSelection=false] Pass `true` to keep previous selection. - * @param {Boolean} [suppressEvent] Pass `true` to prevent firing the - * `{@link #selectionchange}` event. - */ - selectColumn: function(column, keepSelection, suppressEvent) { - var me = this, - selData = me.selected, - view = column.getView(); - // Clear other selection types - if (!selData || !selData.isColumns || selData.view !== view.ownerGrid.view) { - me.resetSelection(true); - me.selected = selData = new Ext.grid.selection.Columns(view); - } - if (!selData.contains(column)) { - if (!keepSelection) { - selData.clear(); - } - selData.add(column); - me.updateHeaderState(); - if (!suppressEvent) { - me.fireSelectionChange(); - } - } - }, - /** - * Deselects a column. - * @param {Ext.grid.column.Column} column Column to deselect. - * @param {Boolean} [suppressEvent] Pass `true` to prevent firing the - * `{@link #selectionchange}` event. - */ - deselectColumn: function(column, suppressEvent) { - var me = this, - selData = me.getSelected(); - if (selData && selData.isColumns && selData.contains(column)) { - selData.remove(column); - me.updateHeaderState(); - if (!suppressEvent) { - me.fireSelectionChange(); - } - } - }, - getSelection: function() { - // API docs are inherited. - // Superclass returns array of selected records - var selData = this.selected; - if (selData && selData.isRows) { - return selData.getRecords(); - } - return []; - }, - destroy: function() { - var me = this, - scrollEls = me.scrollEls; - Ext.destroy(me.gridListeners, me.viewListeners, me.selected, me.navigationListeners, me.extensible); - if (scrollEls) { - Ext.dd.ScrollManager.unregister(scrollEls); - } - me.selected = me.gridListeners = me.viewListeners = me.selectionData = me.navigationListeners = me.scrollEls = null; - me.callParent(); - }, - //------------------------------------------------------------------------- - privates: { - /** - * @property {Object} axesConfigs - * Use when converting the extensible config into a SelectionExtender to create its `axes` config to specify which axes it may extend. - * @private - */ - axesConfigs: { - x: 1, - y: 2, - xy: 3, - both: 3, - "true": 3 - }, - // reserved word MUST be quoted when used an a property name - /** - * @return {Object} - * @private - */ - getViewListeners: function() { - return { - beforerefresh: this.onBeforeViewRefresh, - refresh: this.onViewRefresh, - keyup: { - element: 'el', - fn: this.onViewKeyUp, - scope: this - } - }; - }, - /** - * @private - */ - onViewKeyUp: function(e) { - var sel = this.selected; - // Released the shift key, terminate a keyboard based range selection - if (e.keyCode === e.SHIFT && sel && sel.isRows && sel.getRangeSize()) { - // Copy the drag range into the selected records collection - sel.addRange(); - } - }, - /** - * @private - */ - onColumnsChanged: function() { - var selData = this.selected, - rowRange, colCount, colIdx, rowIdx, view, context, selectionChanged; - // When columns have changed, we have to deselect *every* cell in the row range because we do not know where the - // columns have gone to. - if (selData) { - view = selData.view; - if (selData.isCells) { - context = new Ext.grid.CellContext(view); - rowRange = selData.getRowRange(); - colCount = view.getVisibleColumnManager().getColumns().length; - for (rowIdx = rowRange[0]; rowIdx <= rowRange[1]; rowIdx++) { - context.setRow(rowIdx); - for (colIdx = 0; colIdx < colCount; colIdx++) { - context.setColumn(colIdx); - view.onCellDeselect(context); - } - } - } - // We have to deselect columns which have been hidden/removed - else if (selData.isColumns) { - selectionChanged = false; - selData.eachColumn(function(column, columnIdx) { - if (!column.isVisible() || !view.ownerGrid.isAncestor(column)) { - this.remove(column); - selectionChanged = true; - } - }); - } - } - // This event is fired directly from the HeaderContainer before the view updates. - // So we have to wait until idle to update the selection UI. - // NB: fireSelectionChange calls updateSelectionExtender after firing its event. - Ext.on('idle', selectionChanged ? this.fireSelectionChange : this.updateSelectionExtender, this, { - single: true - }); - }, - // The selection may have acquired or lost contiguity, so the replicator may need enabling or disabling - onColumnMove: function() { - this.updateSelectionExtender(); - }, - /** - * @private - */ - onBeforeViewRefresh: function(view) { - var selData = this.selected; - // Allow cell preselection to survive, but not cell selection from a prior refresh - if (view.refreshCounter) { - if (selData && selData.isCells) { - this.resetSelection(); - } - } - }, - /** - * @private - */ - onViewRefresh: function(view) { - var me = this, - sel = this.selected, - store = me.view.store, - changed = false; - // Deselect filtered out records - if (sel && sel.isRows && store.isFiltered()) { - sel.eachRow(function(rec) { - if (!store.contains(rec)) { - this.remove(rec); - // Maintainer: this is the Rows selection object, *NOT* me. - changed = true; - } - }); - } - // The selection may have acquired or lost contiguity, so the replicator may need enabling or disabling - // NB: fireSelectionChange calls updateSelectionExtender after firing its event. - this[changed ? 'fireSelectionChange' : 'updateSelectionExtender'](); - }, - /** - * @private - */ - resetSelection: function(suppressEvent) { - var sel = this.selected; - if (sel) { - sel.clear(); - if (!suppressEvent) { - this.fireSelectionChange(); - } - } - }, - onViewCreated: function(grid, view) { - var me = this, - ownerGrid = view.ownerGrid, - headerCt = view.headerCt; - // Only add columns to the locked view, or only view if there is no twin - if (!ownerGrid.lockable || view.isLockedView) { - // if there is no row number column and we ask for it, then it should be added here - if (me.getRowSelect()) { - // Ensure we have a rownumber column - me.getNumbererColumn(); - } - if (me.checkboxSelect) { - me.addCheckbox(view, true); - } - me.mon(view.ownerGrid, 'beforereconfigure', me.onBeforeReconfigure, me); - } - // Disable sortOnClick if we're columnSelecting - headerCt.sortOnClick = !me.getColumnSelect(); - if (me.getDragSelect()) { - view.on('render', me.onViewRender, me, { - single: true - }); - } - }, - /** - * Initialize drag selection support - * @private - */ - onViewRender: function(view) { - var me = this, - el = view.getEl(), - views = me.views, - len = views.length, - i; - // If we receive the render event after the columnSelect config has been set, - // ensure that the view's headerCts know not to sort on click if we're selecting columns. - for (i = 0; i < len; i++) { - views[i].headerCt.sortOnClick = !me.columnSelect; - } - el.ddScrollConfig = { - vthresh: 50, - hthresh: 50, - frequency: 300, - increment: 100 - }; - Ext.dd.ScrollManager.register(el); - // Possible two child views to register as scrollable on drag - (me.scrollEls || (me.scrollEls = [])).push(el); - view.on('cellmousedown', me.handleMouseDown, me); - // In a locking situation, we need a mousedown listener on both sides. - if (view.lockingPartner) { - view.lockingPartner.on('cellmousedown', me.handleMouseDown, me); - } - }, - /** - * Plumbing for drag selection of cell range - * @private - */ - handleMouseDown: function(view, td, cellIndex, record, tr, rowIdx, e) { - var me = this, - sel = me.selected, - header = e.position.column, - isCheckClick, startDragSelect; - // Ignore right click and shit and alt modifiers. - // Also ignore touchstart. We cannot drag select using touches. - if (e.button || e.shiftKey || e.altKey || e.pointerType === 'touch') { - return; - } - if (header) { - isCheckClick = header === me.checkColumn; - // Differentiate between row and cell selections. - if (header === me.numbererColumn || isCheckClick || !me.cellSelect) { - // Enforce rowSelect setting - if (me.rowSelect) { - if (sel && sel.isRows) { - if (!e.ctrlKey && !isCheckClick) { - sel.clear(); - } - } else { - if (sel) { - sel.clear(); - } - sel = me.selected = new Ext.grid.selection.Rows(view); - } - startDragSelect = true; - } - } else { - if (sel) { - sel.clear(); - } - if (!sel || !sel.isCells) { - sel = me.selected = new Ext.grid.selection.Cells(view); - } - startDragSelect = true; - } - me.lastOverRecord = me.lastOverColumn = null; - // Add the listener after the view has potentially been corrected - Ext.getBody().on('mouseup', me.onMouseUp, me, { - single: true, - view: sel.view - }); - // Only begin the drag process if configured to select what they asked for - if (startDragSelect) { - sel.view.el.on('mousemove', me.onMouseMove, me, { - view: sel.view - }); - } - } - }, - /** - * Selects range based on mouse movements - * @param e - * @param cell - * @param opts - * @private - */ - onMouseMove: function(e, target, opts) { - var me = this, - view = opts.view, - record, rowIdx, - cell = e.getTarget(view.cellSelector), - header = opts.view.getHeaderByCell(cell), - selData = me.selected, - pos, recChange, colChange; - // Disable until a valid new selection is announced in fireSelectionChange - if (me.extensible) { - me.extensible.disable(); - } - if (header) { - record = view.getRecord(cell.parentNode); - rowIdx = me.store.indexOf(record); - recChange = record !== me.lastOverRecord; - colChange = header !== me.lastOverColumn; - if (recChange || colChange) { - pos = me.getCellContext(record, header); - } - // Initial mousedown was in rownumberer or checkbox column - if (selData.isRows) { - // Only react if we've changed row - if (recChange) { - if (me.lastOverRecord) { - selData.setRangeEnd(rowIdx); - } else { - selData.setRangeStart(rowIdx); - } - } - } else // Selecting cells - { - // Only react if we've changed row or column - if (recChange || colChange) { - if (me.lastOverRecord) { - selData.setRangeEnd(pos); - } else { - selData.setRangeStart(pos); - } - } - } - // Focus MUST follow the mouse. - // Otherwise the focus may scroll out of the rendered range and revert to document - if (recChange || colChange) { - // We MUST pass local view into NavigationModel, not the potentially outermost locking view. - // TODO: When that's fixed, use setPosition(pos). - view.getNavigationModel().setPosition(new Ext.grid.CellContext(header.getView()).setPosition(record, header)); - } - me.lastOverColumn = header; - me.lastOverRecord = record; - } - }, - /** - * Clean up mousemove event - * @param e - * @param target - * @param opts - * @private - */ - onMouseUp: function(e, target, opts) { - var me = this, - view = opts.view; - if (view && !view.destroyed) { - // Disable until a valid new selection is announced in fireSelectionChange - if (me.extensible) { - me.extensible.disable(); - } - view.el.un('mousemove', me.onMouseMove, me); - // Copy the records encompassed by the drag range into the record collection - if (me.selected.isRows) { - me.selected.addRange(); - } - me.fireSelectionChange(); - } - }, - /** - * Add the header checkbox to the header row - * @param view - * @param {Boolean} initial True if we're binding for the first time. - * @private - */ - addCheckbox: function(view, initial) { - var me = this, - checkbox = me.checkboxColumnIndex, - headerCt = view.headerCt; - // Preserve behaviour of false, but not clear why that would ever be done. - if (checkbox !== false) { - if (checkbox === 'first') { - checkbox = 0; - } else if (checkbox === 'last') { - checkbox = headerCt.getColumnCount(); - } - me.checkColumn = headerCt.add(checkbox, me.getCheckboxHeaderConfig()); - } - if (initial !== true) { - view.refresh(); - } - }, - /** - * Called when the grid's Navigation model detects navigation events (`mousedown`, `click` and certain `keydown` events). - * @param {Ext.event.Event} navigateEvent The event which caused navigation. - * @private - */ - onNavigate: function(navigateEvent) { - var me = this, - // Use outermost view. May be lockable - view = navigateEvent.view.ownerGrid.view, - record = navigateEvent.record, - sel = me.selected, - // Create a new Context based upon the outermost View. - // NavigationModel works on local views. TODO: remove this step when NavModel is fixed to use outermost view in locked grid. - // At that point, we can use navigateEvent.position - pos = new Ext.grid.CellContext(view).setPosition(record, navigateEvent.column), - keyEvent = navigateEvent.keyEvent, - keyCode = keyEvent.getKey(), - selectionChanged; - // A Column's processEvent method may set this flag if configured to do so. - if (keyEvent.stopSelection) { - return; - } - // CTRL/Arrow just navigates, does not select - if (keyEvent.ctrlKey && (keyCode === keyEvent.UP || keyCode === keyEvent.LEFT || keyCode === keyEvent.RIGHT || keyCode === keyEvent.DOWN)) { - return; - } - // Click is the mouseup at the end of a multi-cell select swipe; reject. - if (sel && sel.isCells && sel.getCount() > 1 && keyEvent.type === 'click') { - return; - } - // If all selection types are disabled, or it's not a selecting event, return - if (!(me.cellSelect || me.columnSelect || me.rowSelect) || !navigateEvent.record || keyEvent.type === 'mousedown') { - return; - } - // Ctrl/A key - Deselect current selection, or select all if no selection - if (keyEvent.ctrlKey && keyEvent.keyCode === keyEvent.A) { - // No selection, or only one, select all - if (!sel || sel.getCount() < 2) { - me.selectAll(); - } else { - me.deselectAll(); - } - me.updateHeaderState(); - return; - } - if (keyEvent.shiftKey) { - // If the event is in one of the row selecting cells, or cell selecting is turned off - if (pos.column === me.numbererColumn || pos.column === me.checkColumn || !me.cellSelect || (sel && sel.isRows)) { - if (me.rowSelect) { - // Ensure selection object is of the correct type - if (!sel || !sel.isRows || sel.view !== view) { - me.resetSelection(true); - sel = me.selected = new Ext.grid.selection.Rows(view); - } - // First shift - if (!sel.getRangeSize()) { - sel.setRangeStart(navigateEvent.previousRecordIndex || 0); - } - sel.setRangeEnd(navigateEvent.recordIndex); - sel.addRange(); - selectionChanged = true; - } - } else // Navigate event in a normal cell - { - if (me.cellSelect) { - // Ensure selection object is of the correct type - if (!sel || !sel.isCells || sel.view !== view) { - me.resetSelection(true); - sel = me.selected = new Ext.grid.selection.Cells(view); - } - // First shift - if (!sel.getRangeSize()) { - sel.setRangeStart(navigateEvent.previousPosition || me.getCellContext(0, 0)); - } - sel.setRangeEnd(pos); - selectionChanged = true; - } - } - } else { - // If the event is in one of the row selecting cells, or cell selecting is turned off - if (pos.column === me.numbererColumn || pos.column === me.checkColumn || !me.cellSelect) { - if (me.rowSelect) { - // Ensure selection object is of the correct type - if (!sel || !sel.isRows || sel.view !== view) { - me.resetSelection(true); - sel = me.selected = new Ext.grid.selection.Rows(view); - } - if (keyEvent.ctrlKey || pos.column === me.checkColumn) { - if (sel.contains(record)) { - sel.remove(record); - } else { - sel.add(record); - } - } else { - sel.clear(); - sel.add(record); - } - selectionChanged = true; - } - } else // Navigate event in a normal cell - { - if (me.cellSelect) { - // Ensure selection object is of the correct type - if (!sel || !sel.isCells || sel.view !== view) { - me.resetSelection(true); - me.selected = sel = new Ext.grid.selection.Cells(view); - } else { - sel.clear(); - } - sel.setRangeStart(pos); - selectionChanged = true; - } - } - } - // If our configuration allowed selection changes, update check header and fire event - if (selectionChanged) { - if (sel.isRows) { - me.updateHeaderState(); - } - me.fireSelectionChange(); - } - }, - /** - * Check if given record is currently selected. - * - * Used in {@link Ext.view.Table view} rendering to decide upon cell UI treatment. - * @param {Ext.data.Model} record - * @return {Boolean} - * @private - */ - isRowSelected: function(record) { - var me = this, - sel = me.selected; - if (sel && sel.isRows) { - record = Ext.isNumber(record) ? me.store.getAt(record) : record; - return sel.contains(record); - } else { - return false; - } - }, - /** - * Check if given column is currently selected. - * - * @param {Ext.grid.column.Column} column - * @return {Boolean} - * @private - */ - isColumnSelected: function(column) { - var me = this, - sel = me.selected; - if (sel && sel.isColumns) { - return sel.contains(column); - } else { - return false; - } - }, - /** - * Returns true if specified cell within specified view is selected - * - * Used in {@link Ext.view.Table view} rendering to decide upon row UI treatment. - * @param {Ext.grid.View} view - impactful when locked columns are used - * @param {Number} row - row index - * @param {Number} column - column index, within the current view - * - * @return {Boolean} - * @private - */ - isCellSelected: function(view, row, column) { - var me = this, - testPos, - sel = me.selected; - // view MUST be outermost (possible locking) view - view = view.ownerGrid.view; - if (sel) { - if (sel.isColumns) { - if (typeof column === 'number') { - column = view.getVisibleColumnManager().getColumns()[column]; - } - return sel.contains(column); - } - if (sel.isCells) { - testPos = new Ext.grid.CellContext(view).setPosition({ - row: row, - // IMPORTANT: The historic API for columns has been to include hidden columns - // in the index. So we must index into the "all" ColumnManager. - column: column - }); - return sel.contains(testPos); - } - } - return false; - }, - /** - * @private - */ - applySelected: function(selected) { - // Must override base class's applier which creates a Collection - if (selected && !(selected.isRows || selected.isCells || selected.isColumns)) { - Ext.raise('SpreadsheelModel#setSelected must be passed an instance of Ext.grid.selection.Selection'); - } - return selected; - }, - /** - * @private - */ - updateSelected: function(selected, oldSelected) { - var view, columns, len, i, cell; - // Clear old selection. - if (oldSelected) { - oldSelected.clear(); - } - // Update the UI to match the new selection - if (selected && selected.getCount()) { - view = selected.view; - // Rows; update each selected row - if (selected.isRows) { - selected.eachRow(view.onRowSelect, view); - } - // Columns; update the selected columns for all rows - else if (selected.isColumns) { - columns = selected.getColumns(); - len = columns.length; - if (len) { - cell = new Ext.grid.CelContext(view); - view.store.each(function(rec) { - cell.setRow(rec); - for (i = 0; i < len; i++) { - cell.setColumn(columns[i]); - view.onCellSelect(cell); - } - }); - } - } - // Cells; update each selected cell - else if (selected.isCells) { - selected.eachCell(view.onCellSelect, view); - } - } - }, - getNumbererColumn: function(col) { - var me = this, - result = me.numbererColumn, - view = me.view; - if (!result) { - // Always put row selection columns in the locked side if there is one. - if (view.isNormalView) { - view = view.ownerGrid.lockedGrid; - } - result = me.numbererColumn = view.headerCt.down('rownumberer') || view.headerCt.add(0, me.getNumbererColumnConfig()); - } - return result; - }, - getNumbererColumnConfig: function() { - var me = this; - return { - xtype: 'rownumberer', - width: me.rowNumbererHeaderWidth, - editRenderer: ' ', - tdCls: me.rowNumbererTdCls, - cls: me.rowNumbererHeaderCls, - locked: me.hasLockedHeader - }; - }, - /** - * Show/hide the extra column headers depending upon rowSelection. - * @private - */ - updateRowSelect: function(rowSelect) { - var me = this, - sel = me.selected, - view = me.view; - if (view && view.rendered) { - // Always put row selection columns in the locked side if there is one. - if (view.isNormalView) { - view = view.lockingPartner; - } - if (rowSelect) { - if (me.checkColumn) { - me.checkColumn.show(); - } - me.getNumbererColumn().show(); - } else { - if (me.checkColumn) { - me.checkColumn.hide(); - } - if (me.numbererColumn) { - me.numbererColumn.hide(); - } - } - if (!rowSelect && sel && sel.isRows) { - sel.clear(); - me.fireSelectionChange(); - } - } - }, - /** - * Enable/disable the HeaderContainer's sortOnClick in line with column select on - * column click. - * @private - */ - updateColumnSelect: function(columnSelect) { - var me = this, - sel = me.selected, - views = me.views, - len = views ? views.length : 0, - i; - for (i = 0; i < len; i++) { - views[i].headerCt.sortOnClick = !columnSelect; - } - if (!columnSelect && sel && sel.isColumns) { - sel.clear(); - me.fireSelectionChange(); - } - if (columnSelect) { - me.view.ownerGrid.addCls(me.columnSelectCls); - } else { - me.view.ownerGrid.removeCls(me.columnSelectCls); - } - }, - /** - * @private - */ - updateCellSelect: function(cellSelect) { - var me = this, - sel = me.selected; - if (!cellSelect && sel && sel.isCells) { - sel.clear(); - me.fireSelectionChange(); - } - }, - /** - * @private - */ - fireSelectionChange: function() { - var grid = this.view.ownerGrid, - sel = this.selected; - // Inform selection object that we're done - this.updateSelectionExtender(); - grid.fireEvent('selectionchange', grid, sel); - }, - updateSelectionExtender: function() { - var sel = this.selected; - if (sel) { - sel.onSelectionFinish(); - } - }, - /** - * Called when a selection has been made. The selection object's onSelectionFinish calls back into this. - * @param {Ext.grid.selection.Selection} sel The selection object specific to - * the selection performed. - * @param {Ext.grid.CellContext} [firstCell] The left/top most selected cell. - * Will be undefined if the selection is clear. - * @param {Ext.grid.CellContext} [lastCell] The bottom/right most selected cell. - * Will be undefined if the selection is clear. - * @private - */ - onSelectionFinish: function(sel, firstCell, lastCell) { - var extensible = this.getExtensible(); - if (extensible) { - extensible.setHandle(firstCell, lastCell); - } - }, - applyExtensible: function(extensible) { - var me = this; - if (extensible === true || typeof extensible === 'string') { - extensible = { - axes: me.axesConfigs[extensible] - }; - } else { - extensible = Ext.Object.chain(extensible); - } - // don't mutate the user's config - extensible.view = me.selected.view; - return new Ext.grid.selection.SelectionExtender(extensible); - }, - /** - * Called when the SelectionExtender has the mouse released. - * @param {Object} extension An object describing the type and size of extension. - * @param {String} extension.type `"rows"` or `"columns"` - * @param {Ext.grid.CellContext} extension.start The start (top left) cell of the extension area. - * @param {Ext.grid.CellContext} extension.end The end (bottom right) cell of the extension area. - * @param {number} [extension.columns] The number of columns extended (-ve means on the left side). - * @param {number} [extension.rows] The number of rows extended (-ve means on the top side). - * @private - */ - extendSelection: function(extension) { - var me = this, - sel = me.selected; - // Announce that the selection is to be extended, and if no objections, extend it - if (me.view.ownerGrid.fireEvent('beforeselectionextend', me.view.ownerGrid, sel, extension) !== false) { - sel.extendRange(extension); - me.fireSelectionChange(); - } - }, - /** - * @private - */ - onIdChanged: function(store, rec, oldId, newId) { - var sel = this.selected; - if (sel && sel.isRows && sel.selectedRecords) { - sel.selectedRecords.updateKey(rec, oldId); - } - }, - /** - * Called when a page is added to BufferedStore. - * @private - */ - onPageAdd: function(pageMap, pageNumber, records) { - var sel = this.selected, - len = records.length, - i, record, - selected = sel && sel.selectedRecords; - // Check for return of already selected records. - // Maintainer: To only use one conditional expression, the value of assignment of - // (selected = sel.selectedRecords) is part of the single conditional expression. - if (selected && sel.isRows) { - for (i = 0; i < len; i++) { - record = records[i]; - if (selected.get(record.id)) { - selected.replace(record); - } - } - } - }, - /** - * @private - */ - refresh: function() { - var sel = this.getSelected(); - // Refreshing the selected record Collection based upon a possible - // store mutation is only valid if we are selecting records. - if (sel && sel.isRows) { - this.callParent(); - } - }, - /** - * @private - */ - onStoreAdd: function() { - var sel = this.getSelected(); - // Updating on store mutation is only valid if we are selecting records. - if (sel && sel.isRows) { - this.callParent(arguments); - this.updateHeaderState(); - } - }, - /** - * @private - */ - onStoreClear: function() { - this.resetSelection(); - }, - /** - * @private - */ - onStoreLoad: function() { - var sel = this.getSelected(); - // Updating on store mutation is only valid if we are selecting records. - if (sel && sel.isRows) { - this.callParent(arguments); - this.updateHeaderState(); - } - }, - /** - * @private - */ - onStoreRefresh: function() { - var sel = this.selected; - // Ensure that records which are no longer in the new store are pruned if configured to do so. - // Ensure that selected records in the collection are the correct instance. - if (sel && sel.isRows && sel.selectedRecords) { - this.updateSelectedInstances(sel.selectedRecords); - } - this.updateHeaderState(); - }, - /** - * @private - */ - onStoreRemove: function() { - var sel = this.getSelected(); - // Updating on store mutation is only valid if we are selecting records. - if (sel && sel.isRows) { - this.callParent(arguments); - } - } - } -}, function(SpreadsheetModel) { - var RowNumberer = Ext.ClassManager.get('Ext.grid.column.RowNumberer'); - if (RowNumberer) { - SpreadsheetModel.prototype.rowNumbererTdCls = Ext.grid.column.RowNumberer.prototype.tdCls + ' ' + Ext.baseCSSPrefix + 'ssm-row-numberer-cell'; - } -}); - -/** - * An internal Queue class. - * @private - */ -Ext.define('Ext.util.Queue', { - constructor: function() { - this.clear(); - }, - add: function(obj) { - var me = this, - key = me.getKey(obj); - if (!me.map[key]) { - ++me.length; - me.items.push(obj); - me.map[key] = obj; - } - return obj; - }, - /** - * Removes all items from the collection. - */ - clear: function() { - var me = this, - items = me.items; - me.items = []; - me.map = {}; - me.length = 0; - return items; - }, - contains: function(obj) { - var key = this.getKey(obj); - return this.map.hasOwnProperty(key); - }, - /** - * Returns the number of items in the collection. - * @return {Number} the number of items in the collection. - */ - getCount: function() { - return this.length; - }, - getKey: function(obj) { - return obj.id; - }, - /** - * Remove an item from the collection. - * @param {Object} obj The item to remove. - * @return {Object} The item removed or false if no item was removed. - */ - remove: function(obj) { - var me = this, - key = me.getKey(obj), - items = me.items, - index; - if (me.map[key]) { - index = Ext.Array.indexOf(items, obj); - Ext.Array.erase(items, index, 1); - delete me.map[key]; - --me.length; - } - return obj; - } -}); - -/** - * This class manages state information for a component or element during a layout. - * - * # Blocks - * - * A "block" is a required value that is preventing further calculation. When a layout has - * encountered a situation where it cannot possibly calculate results, it can associate - * itself with the context item and missing property so that it will not be rescheduled - * until that property is set. - * - * Blocks are a one-shot registration. Once the property changes, the block is removed. - * - * Be careful with blocks. If *any* further calculations can be made, a block is not the - * right choice. - * - * # Triggers - * - * Whenever any call to {@link #getProp}, {@link #getDomProp}, {@link #hasProp} or - * {@link #hasDomProp} is made, the current layout is automatically registered as being - * dependent on that property in the appropriate state. Any changes to the property will - * trigger the layout and it will be queued in the {@link Ext.layout.Context}. - * - * Triggers, once added, remain for the entire layout. Any changes to the property will - * reschedule all unfinished layouts in their trigger set. - * - * @private - */ -Ext.define('Ext.layout.ContextItem', { - heightModel: null, - widthModel: null, - sizeModel: null, - /** - * There are several cases that allow us to skip (opt out) of laying out a component - * and its children as long as its `lastBox` is not marked as `invalid`. If anything - * happens to change things, the `lastBox` is marked as `invalid` by `updateLayout` - * as it ascends the component hierarchy. - * - * @property {Boolean} optOut - * @private - * @readonly - */ - optOut: false, - ownerSizePolicy: null, - // plaed here by Component.getSizeModel - boxChildren: null, - boxParent: null, - children: [], - dirty: null, - // The number of dirty properties - dirtyCount: 0, - hasRawContent: true, - isContextItem: true, - isTopLevel: false, - consumersContentHeight: 0, - consumersContentWidth: 0, - consumersContainerHeight: 0, - consumersContainerWidth: 0, - consumersHeight: 0, - consumersWidth: 0, - ownerCtContext: null, - remainingChildDimensions: 0, - // the current set of property values: - props: null, - /** - * @property {Object} state - * State variables that are cleared when invalidated. Only applies to component items. - */ - state: null, - /** - * @property {Boolean} wrapsComponent - * True if this item wraps a Component (rather than an Element). - * @readonly - */ - wrapsComponent: false, - constructor: function(config) { - var me = this, - sizeModels = Ext.layout.SizeModel.sizeModels, - configured = sizeModels.configured, - shrinkWrap = sizeModels.shrinkWrap, - el, lastBox, ownerCt, ownerCtContext, props, sizeModel, target, lastWidth, lastHeight, sameWidth, sameHeight, widthModel, heightModel, optOut; - Ext.apply(me, config); - target = me.target; - el = me.el; - me.id = target.id; - // These hold collections of layouts that are either blocked or triggered by sets - // to our properties (either ASAP or after flushing to the DOM). All of them have - // the same structure: - // - // me.blocks = { - // width: { - // 'layout-1001': layout1001 - // } - // } - // - // The property name is the primary key which yields an object keyed by layout id - // with the layout instance as the value. This prevents duplicate entries for one - // layout and gives O(1) access to the layout instance when we need to iterate and - // process them. - // - // me.blocks = {}; - // me.domBlocks = {}; - // me.domTriggers = {}; - // me.triggers = {}; - me.flushedProps = {}; - me.props = props = {}; - // the set of cached styles for the element: - me.styles = {}; - if (!target.isComponent) { - lastBox = el.lastBox; - } else { - me.wrapsComponent = true; - me.framing = target.frameSize || null; - me.isComponentChild = target.ownerLayout && target.ownerLayout.isComponentLayout; - lastBox = target.lastBox; - // These items are created top-down, so the ContextItem of our ownerCt should - // be available (if it is part of this layout run). - ownerCt = target.ownerCt; - if (ownerCt && (ownerCtContext = ownerCt.el && me.context.items[ownerCt.el.id])) { - me.ownerCtContext = ownerCtContext; - } - // If our ownerCtContext is in the run, it will have a SizeModel that we use to - // optimize the determination of our sizeModel. Also see recalculateSizeModel, similar - // logic exists there. - me.sizeModel = sizeModel = target.getSizeModel(ownerCtContext && ownerCtContext.widthModel.pairsByHeightOrdinal[ownerCtContext.heightModel.ordinal]); - // NOTE: The initial determination of sizeModel is valid (thankfully) and is - // needed to cope with adding components to a layout run on-the-fly (e.g., in - // the menu overflow handler of a box layout). Since this is the case, we do - // not need to recompute the sizeModel in init unless it is a "full" init (as - // our ownerCt's sizeModel could have changed in that case). - me.widthModel = widthModel = sizeModel.width; - me.heightModel = heightModel = sizeModel.height; - // The lastBox is populated early but does not get an "invalid" property - // until layout has occurred. The "false" value is placed in the lastBox - // by Component.finishedLayout. - if (lastBox && lastBox.invalid === false) { - sameWidth = (target.width === (lastWidth = lastBox.width)); - sameHeight = (target.height === (lastHeight = lastBox.height)); - if (widthModel === shrinkWrap && heightModel === shrinkWrap) { - optOut = true; - } else if (widthModel === configured && sameWidth) { - optOut = heightModel === shrinkWrap || (heightModel === configured && sameHeight); - } - if (optOut) { - // Flag this component and capture its last size... - me.optOut = true; - props.width = lastWidth; - props.height = lastHeight; - } - } - } - me.lastBox = lastBox; - }, - /** - * Clears all properties on this object except (perhaps) those not calculated by this - * component. This is more complex than it would seem because a layout can decide to - * invalidate its results and run the component's layouts again, but since some of the - * values may be calculated by the container, care must be taken to preserve those - * values. - * - * @param {Boolean} full True if all properties are to be invalidated, false to keep - * those calculated by the ownerCt. - * @return {Mixed} A value to pass as the first argument to {@link #initContinue}. - * @private - */ - init: function(full, options) { - var me = this, - oldProps = me.props, - oldDirty = me.dirty, - ownerCtContext = me.ownerCtContext, - ownerLayout = me.target.ownerLayout, - firstTime = !me.state, - ret = full || firstTime, - children, i, n, ownerCt, sizeModel, target, - oldHeightModel = me.heightModel, - oldWidthModel = me.widthModel, - newHeightModel, newWidthModel, - remainingCount = 0; - me.dirty = me.invalid = false; - me.props = {}; - // Reset the number of child dimensions since the children will add their part: - me.remainingChildDimensions = 0; - if (me.boxChildren) { - me.boxChildren.length = 0; - } - // keep array (more GC friendly) - if (!firstTime) { - me.clearAllBlocks('blocks'); - me.clearAllBlocks('domBlocks'); - } - // For Element wrappers, we are done... - if (!me.wrapsComponent) { - return ret; - } - // From here on, we are only concerned with Component wrappers... - target = me.target; - me.state = {}; - // only Component wrappers need a "state" - if (firstTime) { - // This must occur before we proceed since it can do many things (like add - // child items perhaps): - if (target.beforeLayout && target.beforeLayout !== Ext.emptyFn) { - target.beforeLayout(); - } - // Determine the ownerCtContext if we aren't given one. Normally the firstTime - // we meet a component is before the context is run, but it is possible for - // components to be added to a run that is already in progress. If so, we have - // to lookup the ownerCtContext since the odds are very high that the new - // component is a child of something already in the run. It is currently - // unsupported to drag in the owner of a running component (needs testing). - if (!ownerCtContext && (ownerCt = target.ownerCt)) { - ownerCtContext = me.context.items[ownerCt.el.id]; - } - if (ownerCtContext) { - me.ownerCtContext = ownerCtContext; - me.isBoxParent = ownerLayout && ownerLayout.isItemBoxParent(me); - } else { - me.isTopLevel = true; - } - // this is used by initAnimation... - me.frameBodyContext = me.getEl('frameBody'); - } else { - ownerCtContext = me.ownerCtContext; - // In theory (though untested), this flag can change on-the-fly... - me.isTopLevel = !ownerCtContext; - // Init the children element items since they may have dirty state (no need to - // do this the firstTime). - children = me.children; - for (i = 0 , n = children.length; i < n; ++i) { - children[i].init(true); - } - } - // We need to know how we will determine content size: containers can look at the - // results of their items but non-containers or item-less containers with just raw - // markup need to be measured in the DOM: - me.hasRawContent = !(target.isContainer && target.items.items.length > 0); - if (full) { - // We must null these out or getSizeModel will assume they are the correct, - // dynamic size model and return them (the previous dynamic sizeModel). - me.widthModel = me.heightModel = null; - sizeModel = target.getSizeModel(ownerCtContext && ownerCtContext.widthModel.pairsByHeightOrdinal[ownerCtContext.heightModel.ordinal]); - if (firstTime) { - me.sizeModel = sizeModel; - } - me.widthModel = sizeModel.width; - me.heightModel = sizeModel.height; - // if we are a container child (e.g., not a docked item), and this is a full - // init, that means our parent was invalidated, and therefore we must initialize - // our remainingChildDimensions to ensure that containerChildrenSizeDone - // gets set properly once all dimensions have had their sizes determined. - // There are 3 possible scenarios here: - // - // 1. Layouts that both read and set sizes of their items (e.g. box). These - // layouts must always add both dimensions to remainingChildDimensions. - // - // 2. Layouts that neither read nor set the size of their items (e.g. - // autocontainer, form). These layouts will not create context items for their - // children, and so we will never end up here. - // - // 3. Layouts that may set the size of their items, but will never read them - // because they measure an outer containing element in the shrink-wrapping - // dimension(s) (e.g. anchor, column). There are 2 possible outcomes: - // a. The child item uses liquid CSS layout. In this case, the only dimensions - // that affect containerChildrenSizeDone are the dimensions that the owner - // layout is responsible for calculating, and so these are the dimensions - // that are added to remainingChildDimensions. Non-calculated dimensions will - // never be published because the child's component layout does not run. - // - // b. The child item does not use liquid CSS layout. In this case, the - // component layout will run like normal, and any non-calculated dimensions - // will be published, therefore, we need to add both dimensions to - // remainingChildDimensions - if (ownerCtContext && !me.isComponentChild) { - if (ownerLayout.needsItemSize || !target.liquidLayout) { - ownerCtContext.remainingChildDimensions += 2; - } else { - if (me.widthModel.calculated) { - ++ownerCtContext.remainingChildDimensions; - } - if (me.heightModel.calculated) { - ++ownerCtContext.remainingChildDimensions; - } - } - } - } else if (oldProps) { - // these are almost always calculated by the ownerCt (we might need to track - // this at some point more carefully): - me.recoverProp('x', oldProps, oldDirty); - me.recoverProp('y', oldProps, oldDirty); - // if these are calculated by the ownerCt, don't trash them: - if (me.widthModel.calculated) { - me.recoverProp('width', oldProps, oldDirty); - } else if ('width' in oldProps) { - ++remainingCount; - } - if (me.heightModel.calculated) { - me.recoverProp('height', oldProps, oldDirty); - } else if ('height' in oldProps) { - ++remainingCount; - } - // if we are a container child and this is not a full init, that means our - // parent was not invalidated and therefore only the dimensions that were - // set last time and removed from remainingChildDimensions last time, need to - // be added back to remainingChildDimensions. This only needs to happen for - // properties that we don't recover above (model=calculated) - if (ownerCtContext && !me.isComponentChild) { - ownerCtContext.remainingChildDimensions += remainingCount; - } - } - if (oldProps && ownerLayout && ownerLayout.manageMargins) { - me.recoverProp('margin-top', oldProps, oldDirty); - me.recoverProp('margin-right', oldProps, oldDirty); - me.recoverProp('margin-bottom', oldProps, oldDirty); - me.recoverProp('margin-left', oldProps, oldDirty); - } - // Process any invalidate options present. These can only come from explicit calls - // to the invalidate() method. - if (options) { - // Consider a container box with wrapping text. If the box is made wider, the - // text will take up less height (until there is no more wrapping). Conversely, - // if the box is made narrower, the height starts to increase due to wrapping. - // - // Imposing a minWidth constraint would increase the width. This may decrease - // the height. If the box is shrinkWrap, however, the width will already be - // such that there is no wrapping, so the height will not further decrease. - // Since the height will also not increase if we widen the box, there is no - // problem simultaneously imposing a minHeight or maxHeight constraint. - // - // When we impose as maxWidth constraint, however, we are shrinking the box - // which may increase the height. If we are imposing a maxHeight constraint, - // that is fine because a further increased height will still need to be - // constrained. But if we are imposing a minHeight constraint, we cannot know - // whether the increase in height due to wrapping will be greater than the - // minHeight. If we impose a minHeight constraint at the same time, then, we - // could easily be locking in the wrong height. - // - // It is important to note that this logic applies to simultaneously *adding* - // both a maxWidth and a minHeight constraint. It is perfectly fine to have - // a state with both constraints, but we cannot add them both at once. - newHeightModel = options.heightModel; - newWidthModel = options.widthModel; - if (newWidthModel && newHeightModel && oldWidthModel && oldHeightModel) { - if (oldWidthModel.shrinkWrap && oldHeightModel.shrinkWrap) { - if (newWidthModel.constrainedMax && newHeightModel.constrainedMin) { - newHeightModel = null; - } - } - } - // Apply size model updates (if any) and state updates (if any). - if (newWidthModel) { - me.widthModel = newWidthModel; - } - if (newHeightModel) { - me.heightModel = newHeightModel; - } - if (options.state) { - Ext.apply(me.state, options.state); - } - } - return ret; - }, - /** - * @private - */ - initContinue: function(full) { - var me = this, - ownerCtContext = me.ownerCtContext, - comp = me.target, - widthModel = me.widthModel, - inheritedState = comp.getInherited(), - boxParent; - if (widthModel.fixed) { - // calculated or configured - inheritedState.inShrinkWrapTable = false; - } else { - delete inheritedState.inShrinkWrapTable; - } - if (full) { - if (ownerCtContext && widthModel.shrinkWrap) { - boxParent = ownerCtContext.isBoxParent ? ownerCtContext : ownerCtContext.boxParent; - if (boxParent) { - boxParent.addBoxChild(me); - } - } else if (widthModel.natural) { - me.boxParent = ownerCtContext; - } - } - return full; - }, - /** - * @private - */ - initDone: function(containerLayoutDone) { - var me = this, - props = me.props, - state = me.state; - // These properties are only set when they are true: - if (me.remainingChildDimensions === 0) { - props.containerChildrenSizeDone = true; - } - if (containerLayoutDone) { - props.containerLayoutDone = true; - } - if (me.boxChildren && me.boxChildren.length && me.widthModel.shrinkWrap) { - // set a very large width to allow the children to measure their natural - // widths (this is cleared once all children have been measured): - me.el.setWidth(10000); - // don't run layouts for this component until we clear this width... - state.blocks = (state.blocks || 0) + 1; - } - }, - /** - * @private - */ - initAnimation: function() { - var me = this, - target = me.target, - ownerCtContext = me.ownerCtContext; - if (ownerCtContext && ownerCtContext.isTopLevel) { - // See which properties we are supposed to animate to their new state. - // If there are any, queue ourself to be animated by the owning Context - me.animatePolicy = target.ownerLayout.getAnimatePolicy(me); - } else if (!ownerCtContext && target.isCollapsingOrExpanding && target.animCollapse) { - // Collapsing/expnding a top level Panel with animation. We need to fabricate - // an animatePolicy depending on which dimension the collapse is using, - // isCollapsingOrExpanding is set during the collapse/expand process. - me.animatePolicy = target.componentLayout.getAnimatePolicy(me); - } - if (me.animatePolicy) { - me.context.queueAnimation(me); - } - }, - /** - * Adds a block. - * - * @param {String} name The name of the block list ('blocks' or 'domBlocks'). - * @param {Ext.layout.Layout} layout The layout that is blocked. - * @param {String} propName The property name that blocked the layout (e.g., 'width'). - * @private - */ - addBlock: function(name, layout, propName) { - var me = this, - collection = me[name] || (me[name] = {}), - blockedLayouts = collection[propName] || (collection[propName] = {}); - if (!blockedLayouts[layout.id]) { - blockedLayouts[layout.id] = layout; - ++layout.blockCount; - ++me.context.blockCount; - } - }, - addBoxChild: function(boxChildItem) { - var me = this, - children, - widthModel = boxChildItem.widthModel; - boxChildItem.boxParent = this; - // Children that are widthModel.auto (regardless of heightModel) that measure the - // DOM (by virtue of hasRawContent), need to wait for their "box parent" to be sized. - // If they measure too early, they will be wrong results. In the widthModel.shrinkWrap - // case, the boxParent "crushes" the child. In the case of widthModel.natural, the - // boxParent's width is likely a key part of the child's width (e.g., "50%" or just - // normal block-level behavior of 100% width) - boxChildItem.measuresBox = widthModel.shrinkWrap ? boxChildItem.hasRawContent : widthModel.natural; - if (boxChildItem.measuresBox) { - children = me.boxChildren; - if (children) { - children.push(boxChildItem); - } else { - me.boxChildren = [ - boxChildItem - ]; - } - } - }, - /** - * Adds x and y values from a props object to a styles object as "left" and "top" values. - * Overridden to add the x property as "right" in rtl mode. - * @property {Object} styles A styles object for an Element - * @property {Object} props A ContextItem props object - * @return {Number} count The number of styles that were set. - * @private - */ - addPositionStyles: function(styles, props) { - var x = props.x, - y = props.y, - count = 0; - if (x !== undefined) { - styles.left = x + 'px'; - ++count; - } - if (y !== undefined) { - styles.top = y + 'px'; - ++count; - } - return count; - }, - /** - * Adds a trigger. - * - * @param {String} propName The property name that triggers the layout (e.g., 'width'). - * @param {Boolean} inDom True if the trigger list is `domTriggers`, false if `triggers`. - * @private - */ - addTrigger: function(propName, inDom) { - var me = this, - name = inDom ? 'domTriggers' : 'triggers', - collection = me[name] || (me[name] = {}), - context = me.context, - layout = context.currentLayout, - triggers = collection[propName] || (collection[propName] = {}); - if (!triggers[layout.id]) { - triggers[layout.id] = layout; - ++layout.triggerCount; - triggers = context.triggers[inDom ? 'dom' : 'data']; - (triggers[layout.id] || (triggers[layout.id] = [])).push({ - item: this, - prop: propName - }); - if (me.props[propName] !== undefined) { - if (!inDom || !(me.dirty && (propName in me.dirty))) { - ++layout.firedTriggers; - } - } - } - }, - boxChildMeasured: function() { - var me = this, - state = me.state, - count = (state.boxesMeasured = (state.boxesMeasured || 0) + 1); - if (count === me.boxChildren.length) { - // all of our children have measured themselves, so we can clear the width - // and resume layouts for this component... - state.clearBoxWidth = 1; - ++me.context.progressCount; - me.markDirty(); - } - }, - borderNames: [ - 'border-top-width', - 'border-right-width', - 'border-bottom-width', - 'border-left-width' - ], - marginNames: [ - 'margin-top', - 'margin-right', - 'margin-bottom', - 'margin-left' - ], - paddingNames: [ - 'padding-top', - 'padding-right', - 'padding-bottom', - 'padding-left' - ], - trblNames: [ - 'top', - 'right', - 'bottom', - 'left' - ], - cacheMissHandlers: { - borderInfo: function(me) { - var info = me.getStyles(me.borderNames, me.trblNames); - info.width = info.left + info.right; - info.height = info.top + info.bottom; - return info; - }, - marginInfo: function(me) { - var info = me.getStyles(me.marginNames, me.trblNames); - info.width = info.left + info.right; - info.height = info.top + info.bottom; - return info; - }, - paddingInfo: function(me) { - // if this context item's target is a framed component the padding is on the frameBody, not on the main el - var item = me.frameBodyContext || me, - info = item.getStyles(me.paddingNames, me.trblNames); - info.width = info.left + info.right; - info.height = info.top + info.bottom; - return info; - } - }, - checkCache: function(entry) { - return this.cacheMissHandlers[entry](this); - }, - clearAllBlocks: function(name) { - var collection = this[name], - propName; - if (collection) { - for (propName in collection) { - this.clearBlocks(name, propName); - } - } - }, - /** - * Removes any blocks on a property in the specified set. Any layouts that were blocked - * by this property and are not still blocked (by other properties) will be rescheduled. - * - * @param {String} name The name of the block list ('blocks' or 'domBlocks'). - * @param {String} propName The property name that blocked the layout (e.g., 'width'). - * @private - */ - clearBlocks: function(name, propName) { - var collection = this[name], - blockedLayouts = collection && collection[propName], - context, layout, layoutId; - if (blockedLayouts) { - delete collection[propName]; - context = this.context; - for (layoutId in blockedLayouts) { - layout = blockedLayouts[layoutId]; - --context.blockCount; - if (!--layout.blockCount && !layout.pending && !layout.done) { - context.queueLayout(layout); - } - } - } - }, - /** - * Registers a layout in the block list for the given property. Once the property is - * set in the {@link Ext.layout.Context}, the layout is unblocked. - * - * @param {Ext.layout.Layout} layout - * @param {String} propName The property name that blocked the layout (e.g., 'width'). - */ - block: function(layout, propName) { - this.addBlock('blocks', layout, propName); - }, - /** - * Registers a layout in the DOM block list for the given property. Once the property - * flushed to the DOM by the {@link Ext.layout.Context}, the layout is unblocked. - * - * @param {Ext.layout.Layout} layout - * @param {String} propName The property name that blocked the layout (e.g., 'width'). - */ - domBlock: function(layout, propName) { - this.addBlock('domBlocks', layout, propName); - }, - /** - * Reschedules any layouts associated with a given trigger. - * - * @param {String} name The name of the trigger list ('triggers' or 'domTriggers'). - * @param {String} propName The property name that triggers the layout (e.g., 'width'). - * @private - */ - fireTriggers: function(name, propName) { - var collection = this[name], - triggers = collection && collection[propName], - context = this.context, - layout, layoutId; - if (triggers) { - for (layoutId in triggers) { - layout = triggers[layoutId]; - ++layout.firedTriggers; - if (!layout.done && !layout.blockCount && !layout.pending) { - context.queueLayout(layout); - } - } - } - }, - /** - * Flushes any updates in the dirty collection to the DOM. This is only called if there - * are dirty entries because this object is only added to the flushQueue of the - * {@link Ext.layout.Context} when entries become dirty. - */ - flush: function() { - var me = this, - dirty = me.dirty, - state = me.state, - targetEl = me.el; - me.dirtyCount = 0; - // Set any queued DOM attributes - if ('attributes' in me) { - targetEl.set(me.attributes); - delete me.attributes; - } - // Set any queued DOM HTML content - if ('innerHTML' in me) { - targetEl.innerHTML = me.innerHTML; - delete me.innerHTML; - } - if (state && state.clearBoxWidth) { - state.clearBoxWidth = 0; - me.el.setStyle('width', null); - if (!--state.blocks) { - me.context.queueItemLayouts(me); - } - } - if (dirty) { - delete me.dirty; - me.writeProps(dirty, true); - } - }, - /** - * @private - */ - flushAnimations: function() { - var me = this, - animateFrom = me.previousSize, - target, targetAnim, duration, animateProps, anim, changeCount, j, propsLen, propName, oldValue, newValue; - // Only animate if the Component has been previously layed out: first layout should not animate - if (animateFrom) { - target = me.target; - targetAnim = target.getAnimationProps(); - duration = targetAnim.duration; - animateProps = Ext.Object.getKeys(me.animatePolicy); - // Create an animation block using the targetAnim configuration to provide defaults. - // They may want custom duration, or easing, or listeners. - anim = Ext.apply({}, { - from: {}, - to: {}, - duration: duration || Ext.fx.Anim.prototype.duration - }, targetAnim); - for (changeCount = 0 , j = 0 , propsLen = animateProps.length; j < propsLen; j++) { - propName = animateProps[j]; - oldValue = animateFrom[propName]; - newValue = me.peek(propName); - if (oldValue !== newValue) { - propName = me.translateProps[propName] || propName; - anim.from[propName] = oldValue; - anim.to[propName] = newValue; - ++changeCount; - } - } - // If any values have changed, kick off animation from the cached old values to the new values - if (changeCount) { - // It'a Panel being collapsed. rollback, and then fix the class name string - if (me.isCollapsingOrExpanding === 1) { - target.componentLayout.undoLayout(me); - } else // Otherwise, undo just the animated properties so the animation can proceed from the old layout. - { - me.writeProps(anim.from); - } - me.el.animate(anim); - anim = Ext.fx.Manager.getFxQueue(me.el.id)[0]; - target.$layoutAnim = anim; - anim.on({ - afteranimate: function() { - delete target.$layoutAnim; - // afteranimate can fire when the target is being destroyed - // and the animation queue is being stopped. - if (target.destroying || target.destroyed) { - return; - } - if (me.isCollapsingOrExpanding === 1) { - target.componentLayout.redoLayout(me); - target.afterCollapse(true); - } else if (me.isCollapsingOrExpanding === 2) { - target.afterExpand(true); - } - if (target.hasListeners.afterlayoutanimation) { - target.fireEvent('afterlayoutanimation', target); - } - } - }); - } - } - }, - /** - * Gets the border information for the element as an object with left, top, right and - * bottom properties holding border size in pixels. This object is only read from the - * DOM on first request and is cached. - * @return {Object} - */ - getBorderInfo: function() { - var me = this, - info = me.borderInfo; - if (!info) { - me.borderInfo = info = me.checkCache('borderInfo'); - } - return info; - }, - /** - * @member Ext.layout.ContextItem - * Returns the context item for an owned element. This should only be called on a - * component's item. The list of child items is used to manage invalidating calculated - * results. - * @param {String/Ext.dom.Element} nameOrEl The element or the name of an owned element - * @param {Ext.layout.container.Container/Ext.Component} [owner] The owner of the - * named element if the passed "nameOrEl" parameter is a String. Defaults to this - * ContextItem's "target" property. For more details on owned elements see - * {@link Ext.Component#cfg-childEls childEls} and - * {@link Ext.Component#renderSelectors renderSelectors} - * @return {Ext.layout.ContextItem} - */ - getEl: function(nameOrEl, owner) { - var me = this, - src, el, elContext; - if (nameOrEl) { - if (nameOrEl.dom) { - el = nameOrEl; - } else { - src = me.target; - if (owner) { - src = owner; - } - el = src[nameOrEl]; - if (typeof el === 'function') { - // ex 'getTarget' - el = el.call(src); - if (el === me.el) { - return this; - } - } - } - // comp.getTarget() often returns comp.el - if (el) { - elContext = me.context.getEl(me, el); - } - } - return elContext || null; - }, - /** - * Gets the "frame" information for the element as an object with left, top, right and - * bottom properties holding border+framing size in pixels. This object is calculated - * on first request and is cached. - * @return {Object} - */ - getFrameInfo: function() { - var me = this, - info = me.frameInfo, - framing, border; - if (!info) { - framing = me.framing; - border = me.getBorderInfo(); - me.frameInfo = info = framing ? { - top: framing.top + border.top, - right: framing.right + border.right, - bottom: framing.bottom + border.bottom, - left: framing.left + border.left, - width: framing.width + border.width, - height: framing.height + border.height - } : border; - } - return info; - }, - /** - * Gets the margin information for the element as an object with left, top, right and - * bottom properties holding margin size in pixels. This object is only read from the - * DOM on first request and is cached. - * @return {Object} - */ - getMarginInfo: function() { - var me = this, - info = me.marginInfo, - comp, manageMargins, ownerLayout, ownerLayoutId; - if (!info) { - if (!me.wrapsComponent) { - info = me.checkCache('marginInfo'); - } else { - comp = me.target; - ownerLayout = comp.ownerLayout; - ownerLayoutId = ownerLayout ? ownerLayout.id : null; - manageMargins = ownerLayout && ownerLayout.manageMargins; - // TODO: stop caching margin$ on the component EXTJS-13359 - info = comp.margin$; - if (info && info.ownerId !== ownerLayoutId) { - // got one but from the wrong owner - info = null; - } - if (!info) { - // if (no cache) - // CSS margins are only checked if there isn't a margin property on the component - info = me.parseMargins(comp, comp.margin) || me.checkCache('marginInfo'); - if (manageMargins) { - // TODO: Stop zeroing out the margins EXTJS-13359 - me.setProp('margin-top', 0); - me.setProp('margin-right', 0); - me.setProp('margin-bottom', 0); - me.setProp('margin-left', 0); - } - // cache the layout margins and tag them with the layout id: - info.ownerId = ownerLayoutId; - comp.margin$ = info; - } - info.width = info.left + info.right; - info.height = info.top + info.bottom; - } - me.marginInfo = info; - } - return info; - }, - /** - * clears the margin cache so that marginInfo get re-read from the dom on the next call to getMarginInfo() - * This is needed in some special cases where the margins have changed since the last layout, making the cached - * values invalid. For example collapsed window headers have different margin than expanded ones. - */ - clearMarginCache: function() { - delete this.marginInfo; - delete this.target.margin$; - }, - /** - * Gets the padding information for the element as an object with left, top, right and - * bottom properties holding padding size in pixels. This object is only read from the - * DOM on first request and is cached. - * @return {Object} - */ - getPaddingInfo: function() { - var me = this, - info = me.paddingInfo; - if (!info) { - me.paddingInfo = info = me.checkCache('paddingInfo'); - } - return info; - }, - /** - * Gets a property of this object. Also tracks the current layout as dependent on this - * property so that changes to it will trigger the layout to be recalculated. - * @param {String} propName The property name that blocked the layout (e.g., 'width'). - * @return {Object} The property value or undefined if not yet set. - */ - getProp: function(propName) { - var me = this, - result = me.props[propName]; - me.addTrigger(propName); - return result; - }, - /** - * Gets a property of this object if it is correct in the DOM. Also tracks the current - * layout as dependent on this property so that DOM writes of it will trigger the - * layout to be recalculated. - * @param {String} propName The property name (e.g., 'width'). - * @return {Object} The property value or undefined if not yet set or is dirty. - */ - getDomProp: function(propName) { - var me = this, - result = (me.dirty && (propName in me.dirty)) ? undefined : me.props[propName]; - me.addTrigger(propName, true); - return result; - }, - /** - * Returns a style for this item. Each style is read from the DOM only once on first - * request and is then cached. If the value is an integer, it is parsed automatically - * (so '5px' is not returned, but rather 5). - * - * @param {String} styleName The CSS style name. - * @return {Object} The value of the DOM style (parsed as necessary). - */ - getStyle: function(styleName) { - var me = this, - styles = me.styles, - info, value; - if (styleName in styles) { - value = styles[styleName]; - } else { - info = me.styleInfo[styleName]; - value = me.el.getStyle(styleName); - if (info && info.parseInt) { - value = parseInt(value, 10) || 0; - } - styles[styleName] = value; - } - return value; - }, - /** - * Returns styles for this item. Each style is read from the DOM only once on first - * request and is then cached. If the value is an integer, it is parsed automatically - * (so '5px' is not returned, but rather 5). - * - * @param {String[]} styleNames The CSS style names. - * @param {String[]} [altNames] The alternate names for the returned styles. If given, - * these names must correspond one-for-one to the `styleNames`. - * @return {Object} The values of the DOM styles (parsed as necessary). - */ - getStyles: function(styleNames, altNames) { - var me = this, - styleCache = me.styles, - values = {}, - hits = 0, - n = styleNames.length, - i, missing, missingAltNames, name, info, styleInfo, styles, value; - altNames = altNames || styleNames; - // We are optimizing this for all hits or all misses. If we hit on all styles, we - // don't create a missing[]. If we miss on all styles, we also don't create one. - for (i = 0; i < n; ++i) { - name = styleNames[i]; - if (name in styleCache) { - values[altNames[i]] = styleCache[name]; - ++hits; - if (i && hits === 1) { - // if (first hit was after some misses) - missing = styleNames.slice(0, i); - missingAltNames = altNames.slice(0, i); - } - } else if (hits) { - (missing || (missing = [])).push(name); - (missingAltNames || (missingAltNames = [])).push(altNames[i]); - } - } - if (hits < n) { - missing = missing || styleNames; - missingAltNames = missingAltNames || altNames; - styleInfo = me.styleInfo; - styles = me.el.getStyle(missing); - for (i = missing.length; i--; ) { - name = missing[i]; - info = styleInfo[name]; - value = styles[name]; - if (info && info.parseInt) { - value = parseInt(value, 10) || 0; - } - values[missingAltNames[i]] = value; - styleCache[name] = value; - } - } - return values; - }, - /** - * Returns true if the given property has been set. This is equivalent to calling - * {@link #getProp} and not getting an undefined result. In particular, this call - * registers the current layout to be triggered by changes to this property. - * - * @param {String} propName The property name (e.g., 'width'). - * @return {Boolean} - */ - hasProp: function(propName) { - return this.getProp(propName) != null; - }, - /** - * Returns true if the given property is correct in the DOM. This is equivalent to - * calling {@link #getDomProp} and not getting an undefined result. In particular, - * this call registers the current layout to be triggered by flushes of this property. - * - * @param {String} propName The property name (e.g., 'width'). - * @return {Boolean} - */ - hasDomProp: function(propName) { - return this.getDomProp(propName) != null; - }, - /** - * Invalidates the component associated with this item. The layouts for this component - * and all of its contained items will be re-run after first clearing any computed - * values. - * - * If state needs to be carried forward beyond the invalidation, the `options` parameter - * can be used. - * - * @param {Object} options An object describing how to handle the invalidation. - * @param {Object} options.state An object to {@link Ext#apply} to the {@link #state} - * of this item after invalidation clears all other properties. - * @param {Function} options.before A function to call after the context data is cleared - * and before the {@link Ext.layout.Layout#beginLayoutCycle} methods are called. - * @param {Ext.layout.ContextItem} options.before.item This ContextItem. - * @param {Object} options.before.options The options object passed to {@link #invalidate}. - * @param {Function} options.after A function to call after the context data is cleared - * and after the {@link Ext.layout.Layout#beginLayoutCycle} methods are called. - * @param {Ext.layout.ContextItem} options.after.item This ContextItem. - * @param {Object} options.after.options The options object passed to {@link #invalidate}. - * @param {Object} options.scope The scope to use when calling the callback functions. - */ - invalidate: function(options) { - this.context.queueInvalidate(this, options); - }, - markDirty: function() { - if (++this.dirtyCount === 1) { - // our first dirty property... queue us for flush - this.context.queueFlush(this); - } - }, - onBoxMeasured: function() { - var boxParent = this.boxParent, - state = this.state; - if (boxParent && boxParent.widthModel.shrinkWrap && !state.boxMeasured && this.measuresBox) { - // since an autoWidth boxParent is holding a width on itself to allow each - // child to measure - state.boxMeasured = 1; - // best to only call once per child - boxParent.boxChildMeasured(); - } - }, - parseMargins: function(comp, margins) { - if (margins === true) { - margins = 5; - } - var type = typeof margins, - ret; - if (type === 'string' || type === 'number') { - ret = comp.parseBox(margins); - } else if (margins) { - ret = { - top: 0, - right: 0, - bottom: 0, - left: 0 - }; - // base defaults - if (margins) { - margins = Ext.apply(ret, comp.parseBox(margins)); - } - } - // + config - return ret; - }, - peek: function(propName) { - return this.props[propName]; - }, - recalculateSizeModel: function() { - // See the constructor, this logic is very similar. Not broken out into - // a separate method for performance reasons - var me = this, - target = me.target, - componentLayout = target.componentLayout, - ownerCtContext = me.ownerCtContext, - oldContext = componentLayout.ownerContext, - sizeModel; - // If the componentLayout has an ownerContext, it will just use the sizeModel that - // exists on the context. Instead, force it to recalculate - componentLayout.ownerContext = null; - me.sizeModel = sizeModel = target.getSizeModel(ownerCtContext && ownerCtContext.widthModel.pairsByHeightOrdinal[ownerCtContext.heightModel.ordinal]); - me.widthModel = sizeModel.width; - me.heightModel = sizeModel.height; - if (oldContext) { - componentLayout.ownerContext = me; - } - }, - /** - * Recovers a property value from the last computation and restores its value and - * dirty state. - * - * @param {String} propName The name of the property to recover. - * @param {Object} oldProps The old "props" object from which to recover values. - * @param {Object} oldDirty The old "dirty" object from which to recover state. - */ - recoverProp: function(propName, oldProps, oldDirty) { - var me = this, - props = me.props, - dirty; - if (propName in oldProps) { - props[propName] = oldProps[propName]; - if (oldDirty && propName in oldDirty) { - dirty = me.dirty || (me.dirty = {}); - dirty[propName] = oldDirty[propName]; - } - } - }, - redo: function(deep) { - var me = this, - items, len, i; - me.revertProps(me.props); - if (deep && me.wrapsComponent) { - // Rollback the state of child Components - if (me.childItems) { - for (i = 0 , items = me.childItems , len = items.length; i < len; i++) { - items[i].redo(deep); - } - } - // Rollback the state of child Elements - for (i = 0 , items = me.children , len = items.length; i < len; i++) { - items[i].redo(); - } - } - }, - /** - * Removes a cached ContextItem that was created using {@link #getEl}. It may be - * necessary to call this method if the dom reference for owned element changes so - * that {@link #getEl} can be called again to reinitialize the ContextItem with the - * new element. - * @param {String/Ext.dom.Element} nameOrEl The element or the name of an owned element - * @param {Ext.layout.container.Container/Ext.Component} [owner] The owner of the - * named element if the passed "nameOrEl" parameter is a String. Defaults to this - * ContextItem's "target" property. - */ - removeEl: function(nameOrEl, owner) { - var me = this, - src, el; - if (nameOrEl) { - if (nameOrEl.dom) { - el = nameOrEl; - } else { - src = me.target; - if (owner) { - src = owner; - } - el = src[nameOrEl]; - if (typeof el === 'function') { - // ex 'getTarget' - el = el.call(src); - if (el === me.el) { - return this; - } - } - } - // comp.getTarget() often returns comp.el - if (el) { - me.context.removeEl(el, me); - } - } - }, - revertProps: function(props) { - var name, - flushed = this.flushedProps, - reverted = {}; - for (name in props) { - if (flushed.hasOwnProperty(name)) { - reverted[name] = props[name]; - } - } - this.writeProps(reverted); - }, - /** - * Queue the setting of a DOM attribute on this ContextItem's target when next flushed. - */ - setAttribute: function(name, value) { - var me = this; - if (!me.attributes) { - me.attributes = {}; - } - me.attributes[name] = value; - me.markDirty(); - }, - setBox: function(box) { - var me = this; - if ('left' in box) { - me.setProp('x', box.left); - } - if ('top' in box) { - me.setProp('y', box.top); - } - // if sizeModel says we should not be setting these, the appropriate calls will be - // null operations... otherwise, we must set these values, so what we have in box - // is what we go with (undefined, NaN and no change are handled at a lower level): - me.setSize(box.width, box.height); - }, - /** - * Sets the contentHeight property. If the component uses raw content, then only the - * measured height is acceptable. - * - * Calculated values can sometimes be NaN or undefined, which generally mean the - * calculation is not done. To indicate that such as value was passed, 0 is returned. - * Otherwise, 1 is returned. - * - * If the caller is not measuring (i.e., they are calculating) and the component has raw - * content, 1 is returned indicating that the caller is done. - */ - setContentHeight: function(height, measured) { - if (!measured && this.hasRawContent) { - return 1; - } - return this.setProp('contentHeight', height); - }, - /** - * Sets the contentWidth property. If the component uses raw content, then only the - * measured width is acceptable. - * - * Calculated values can sometimes be NaN or undefined, which generally means that the - * calculation is not done. To indicate that such as value was passed, 0 is returned. - * Otherwise, 1 is returned. - * - * If the caller is not measuring (i.e., they are calculating) and the component has raw - * content, 1 is returned indicating that the caller is done. - */ - setContentWidth: function(width, measured) { - if (!measured && this.hasRawContent) { - return 1; - } - return this.setProp('contentWidth', width); - }, - /** - * Sets the contentWidth and contentHeight properties. If the component uses raw content, - * then only the measured values are acceptable. - * - * Calculated values can sometimes be NaN or undefined, which generally means that the - * calculation is not done. To indicate that either passed value was such a value, false - * returned. Otherwise, true is returned. - * - * If the caller is not measuring (i.e., they are calculating) and the component has raw - * content, true is returned indicating that the caller is done. - */ - setContentSize: function(width, height, measured) { - return this.setContentWidth(width, measured) + this.setContentHeight(height, measured) === 2; - }, - /** - * Sets a property value. This will unblock and/or trigger dependent layouts if the - * property value is being changed. Values of NaN and undefined are not accepted by - * this method. - * - * @param {String} propName The property name (e.g., 'width'). - * @param {Object} value The new value of the property. - * @param {Boolean} dirty Optionally specifies if the value is currently in the DOM - * (default is `true` which indicates the value is not in the DOM and must be flushed - * at some point). - * @return {Number} 1 if this call specified the property value, 0 if not. - */ - setProp: function(propName, value, dirty) { - var me = this, - valueType = typeof value, - info; - if (valueType === 'undefined' || (valueType === 'number' && isNaN(value))) { - return 0; - } - if (me.props[propName] === value) { - return 1; - } - me.props[propName] = value; - ++me.context.progressCount; - if (dirty === false) { - // if the prop is equivalent to what is in the DOM (we won't be writing it), - // we need to clear hard blocks (domBlocks) on that property. - me.fireTriggers('domTriggers', propName); - me.clearBlocks('domBlocks', propName); - } else { - info = me.styleInfo[propName]; - if (info) { - if (!me.dirty) { - me.dirty = {}; - } - me.dirty[propName] = value; - me.markDirty(); - } - } - // we always clear soft blocks on set - me.fireTriggers('triggers', propName); - me.clearBlocks('blocks', propName); - return 1; - }, - /** - * Sets the height and constrains the height to min/maxHeight range. - * - * @param {Number} height The height. - * @param {Boolean} [dirty=true] Specifies if the value is currently in the DOM. A - * value of `false` indicates that the value is already in the DOM. - * @return {Number} The actual height after constraining. - */ - setHeight: function(height, dirty) /*, private {Boolean} force */ - { - var me = this, - comp = me.target, - ownerCtContext = me.ownerCtContext, - frameBody, frameInfo, min, oldHeight, rem; - if (height < 0) { - height = 0; - } - if (!me.wrapsComponent) { - if (!me.setProp('height', height, dirty)) { - return NaN; - } - } else { - min = me.collapsedVert ? 0 : (comp.minHeight || 0); - height = Ext.Number.constrain(height, min, comp.maxHeight); - oldHeight = me.props.height; - if (!me.setProp('height', height, dirty)) { - return NaN; - } - // if we are a container child, since the height is now known we can decrement - // the number of remainingChildDimensions that the ownerCtContext is waiting on. - if (ownerCtContext && !me.isComponentChild && isNaN(oldHeight)) { - rem = --ownerCtContext.remainingChildDimensions; - if (!rem) { - // if there are 0 remainingChildDimensions set containerChildrenSizeDone - // on the ownerCtContext to indicate that all of its children's dimensions - // are known - ownerCtContext.setProp('containerChildrenSizeDone', true); - } - } - frameBody = me.frameBodyContext; - if (frameBody) { - frameInfo = me.getFrameInfo(); - frameBody[me.el.vertical ? 'setWidth' : 'setHeight'](height - frameInfo.height, dirty); - } - } - return height; - }, - /** - * Sets the height and constrains the width to min/maxWidth range. - * - * @param {Number} width The width. - * @param {Boolean} [dirty=true] Specifies if the value is currently in the DOM. A - * value of `false` indicates that the value is already in the DOM. - * @return {Number} The actual width after constraining. - */ - setWidth: function(width, dirty) /*, private {Boolean} force */ - { - var me = this, - comp = me.target, - ownerCtContext = me.ownerCtContext, - frameBody, frameInfo, min, oldWidth, rem; - if (width < 0) { - width = 0; - } - if (!me.wrapsComponent) { - if (!me.setProp('width', width, dirty)) { - return NaN; - } - } else { - min = me.collapsedHorz ? 0 : (comp.minWidth || 0); - width = Ext.Number.constrain(width, min, comp.maxWidth); - oldWidth = me.props.width; - if (!me.setProp('width', width, dirty)) { - return NaN; - } - // if we are a container child, since the width is now known we can decrement - // the number of remainingChildDimensions that the ownerCtContext is waiting on. - if (ownerCtContext && !me.isComponentChild && isNaN(oldWidth)) { - rem = --ownerCtContext.remainingChildDimensions; - if (!rem) { - // if there are 0 remainingChildDimensions set containerChildrenSizeDone - // on the ownerCtContext to indicate that all of its children's dimensions - // are known - ownerCtContext.setProp('containerChildrenSizeDone', true); - } - } - //if ((frameBody = me.target.frameBody) && (frameBody = me.getEl(frameBody))){ - frameBody = me.frameBodyContext; - if (frameBody) { - frameInfo = me.getFrameInfo(); - frameBody.setWidth(width - frameInfo.width, dirty); - } - } - /*if (owner.frameBody) { - frameContext = ownerContext.frameContext || - (ownerContext.frameContext = ownerContext.getEl('frameBody')); - width += (frameContext.paddingInfo || frameContext.getPaddingInfo()).width; - }*/ - return width; - }, - setSize: function(width, height, dirty) { - this.setWidth(width, dirty); - this.setHeight(height, dirty); - }, - translateProps: { - x: 'left', - y: 'top' - }, - undo: function(deep) { - var me = this, - items, len, i; - me.revertProps(me.lastBox); - if (deep && me.wrapsComponent) { - // Rollback the state of child Components - if (me.childItems) { - for (i = 0 , items = me.childItems , len = items.length; i < len; i++) { - items[i].undo(deep); - } - } - // Rollback the state of child Elements - for (i = 0 , items = me.children , len = items.length; i < len; i++) { - items[i].undo(); - } - } - }, - unsetProp: function(propName) { - var dirty = this.dirty; - delete this.props[propName]; - if (dirty) { - delete dirty[propName]; - } - }, - writeProps: function(dirtyProps, flushing) { - if (!(dirtyProps && typeof dirtyProps === 'object')) { - Ext.Logger.warn('writeProps expected dirtyProps to be an object'); - return; - } - var me = this, - el = me.el, - styles = {}, - styleCount = 0, - // used as a boolean, the exact count doesn't matter - styleInfo = me.styleInfo, - info, propName, numericValue, - width = dirtyProps.width, - height = dirtyProps.height, - target = me.target, - hasWidth, hasHeight, isAbsolute, scrollbarSize, style, targetEl; - // Process non-style properties: - if ('displayed' in dirtyProps) { - el.setDisplayed(dirtyProps.displayed); - } - // Unblock any hard blocks (domBlocks) and copy dom styles into 'styles' - for (propName in dirtyProps) { - if (flushing) { - me.fireTriggers('domTriggers', propName); - me.clearBlocks('domBlocks', propName); - me.flushedProps[propName] = 1; - } - info = styleInfo[propName]; - if (info && info.dom) { - // Numeric dirty values should have their associated suffix added - if (info.suffix && (numericValue = parseInt(dirtyProps[propName], 10))) { - styles[propName] = numericValue + info.suffix; - } else // Non-numeric (eg "auto") go in unchanged. - { - styles[propName] = dirtyProps[propName]; - } - ++styleCount; - } - } - // convert x/y into setPosition (for a component) or left/top styles (for an el) - if ('x' in dirtyProps || 'y' in dirtyProps) { - if (target.isComponent) { - target.setPosition(dirtyProps.x, dirtyProps.y); - } else { - // we wrap an element, so convert x/y to styles: - styleCount += me.addPositionStyles(styles, dirtyProps); - } - } - // IE9 subtracts the scrollbar size from the element size when the element - // is absolutely positioned and uses box-sizing: border-box. To workaround this - // issue we have to add the the scrollbar size. - // - // See http://social.msdn.microsoft.com/Forums/da-DK/iewebdevelopment/thread/47c5148f-a142-4a99-9542-5f230c78cb3b - // - if (me.wrapsComponent && Ext.isIE9) { - // when we set a width and we have a vertical scrollbar (overflowY), we need - // to add the scrollbar width... conversely for the height and overflowX - if ((hasWidth = width !== undefined && me.hasOverflowY) || (hasHeight = height !== undefined && me.hasOverflowX)) { - // check that the component is absolute positioned. - isAbsolute = me.isAbsolute; - if (isAbsolute === undefined) { - isAbsolute = false; - targetEl = me.target.getTargetEl(); - style = targetEl.getStyle('position'); - me.isAbsolute = isAbsolute = (style === 'absolute'); - } - // cache it - if (isAbsolute) { - scrollbarSize = Ext.getScrollbarSize(); - if (hasWidth) { - width = parseInt(width, 10) + scrollbarSize.width; - styles.width = width + 'px'; - ++styleCount; - } - if (hasHeight) { - height = parseInt(height, 10) + scrollbarSize.height; - styles.height = height + 'px'; - ++styleCount; - } - } - } - } - // we make only one call to setStyle to allow it to optimize itself: - if (styleCount) { - el.setStyle(styles); - } - }, - //------------------------------------------------------------------------- - // Diagnostics - debugHooks: { - $enabled: false, - // Disable by default - addBlock: function(name, layout, propName) { - //Ext.log(this.id,'.',propName,' ',name,': ',this.context.getLayoutName(layout)); - (layout.blockedBy || (layout.blockedBy = {}))[this.id + '.' + propName + (name.substring(0, 3) === 'dom' ? ':dom' : '')] = 1; - return this.callParent(arguments); - }, - addBoxChild: function(boxChildItem) { - var ret = this.callParent(arguments), - boxChildren = this.boxChildren, - boxParents; - if (boxChildren && boxChildren.length === 1) { - // the boxParent collection is created by the run override found in - // Ext.diag.layout.Context, but IE sometimes does not load that override, so - // we work around it for now - boxParents = this.context.boxParents || (this.context.boxParents = new Ext.util.MixedCollection()); - boxParents.add(this); - } - return ret; - }, - addTrigger: function(propName, inDom) { - var layout = this.context.currentLayout, - triggers; - //Ext.log(this.id,'.',propName,' ',inDom ? ':dom' : '',' ',this.context.getLayoutName(layout)); - this.callParent(arguments); - triggers = this.context.triggersByLayoutId; - (triggers[layout.id] || (triggers[layout.id] = {}))[this.id + '.' + propName + (inDom ? ':dom' : '')] = { - item: this, - name: propName - }; - }, - checkAuthority: function(prop) { - var me = this, - model = me[prop + 'Model'], - // not me.sizeModel[prop] since it is immutable - layout = me.context.currentLayout, - ok, setBy; - if (layout === me.target.ownerLayout) { - // the ownerLayout is only allowed to set calculated dimensions - ok = model.calculated; - } else if (layout.isComponentLayout) { - // the component's componentLayout (normally) is only allowed to set auto or - // configured dimensions. The exception is when a component is run w/o its - // ownerLayout in the picture (isTopLevel), someone must publish the lastBox - // values and that lucky layout is the componentLayout (kinda had to be since - // the ownerLayout is not running) - ok = me.isTopLevel || model.auto || model.configured; - } - if (!ok) { - setBy = me.context.getLayoutName(layout); - Ext.log(setBy + ' cannot set ' + prop); - } - }, - clearBlocks: function(name, propName) { - var collection = this[name], - blockedLayouts = collection && collection[propName], - key = this.id + '.' + propName + (name.substring(0, 3) === 'dom' ? ':dom' : ''), - layout, layoutId; - if (blockedLayouts) { - for (layoutId in blockedLayouts) { - layout = blockedLayouts[layoutId]; - delete layout.blockedBy[key]; - } - } - return this.callParent(arguments); - }, - getEl: function(el) { - var child = this.callParent(arguments); - if (child && child !== this && child.parent !== this) { - Ext.raise({ - msg: 'Got element from wrong component' - }); - } - return child; - }, - init: function() { - var me = this, - ret; - ret = me.callParent(arguments); - if (me.context.logOn.initItem) { - Ext.log(me.id, ' consumers: content=', me.consumersContentWidth, '/', me.consumersContentHeight, ', container=', me.consumersContainerWidth, '/', me.consumersContainerHeight, ', size=', me.consumersWidth, '/', me.consumersHeight); - } - return ret; - }, - invalidate: function() { - if (this.wrapsComponent) { - if (this.context.logOn.invalidate) { - Ext.log('invalidate: ', this.id); - } - } else { - Ext.raise({ - msg: 'Cannot invalidate an element contextItem' - }); - } - return this.callParent(arguments); - }, - setProp: function(propName, value, dirty) { - var me = this, - layout = me.context.currentLayout, - setBy = me.context.getLayoutName(layout), - fullName = me.id + '.' + propName, - setByProps; - if (value !== null) { - setByProps = me.setBy || (me.setBy = {}); - if (!setByProps[propName]) { - setByProps[propName] = setBy; - } else if (setByProps[propName] !== setBy) { - Ext.log({ - level: 'warn' - }, 'BAD! ', fullName, ' set by ', setByProps[propName], ' and ', setBy); - } - } - if (me.context.logOn.setProp) { - if (typeof value !== 'undefined' && !isNaN(value) && me.props[propName] !== value) { - Ext.log('set ', fullName, ' = ', value, ' (', dirty, ')'); - } - } - return this.callParent(arguments); - }, - setHeight: function(height, dirty, /* private */ - force) { - if (!force && this.wrapsComponent) { - this.checkAuthority('height'); - } - return this.callParent(arguments); - }, - setWidth: function(width, dirty, /* private */ - force) { - if (!force && this.wrapsComponent) { - this.checkAuthority('width'); - } - return this.callParent(arguments); - } - } -}, // End Diagnostics -//------------------------------------------------------------------------- -function() { - var px = { - dom: true, - parseInt: true, - suffix: 'px' - }, - isDom = { - dom: true - }, - faux = { - dom: false - }; - // If a property exists in styleInfo, it participates in some way with the DOM. It may - // be virtualized (like 'x' and y') and be indirect, but still requires a flush cycle - // to reach the DOM. Properties (like 'contentWidth' and 'contentHeight') have no real - // presence in the DOM and hence have no flush intanglements. - // - // For simple styles, the object value on the right contains properties that help in - // decoding values read by getStyle and preparing values to pass to setStyle. - // - this.prototype.styleInfo = { - containerChildrenSizeDone: faux, - containerLayoutDone: faux, - displayed: faux, - done: faux, - x: faux, - y: faux, - // For Ext.grid.ColumnLayout - columnsChanged: faux, - rowHeights: faux, - viewOverflowY: faux, - left: px, - top: px, - right: px, - bottom: px, - width: px, - height: px, - 'border-top-width': px, - 'border-right-width': px, - 'border-bottom-width': px, - 'border-left-width': px, - 'margin-top': px, - 'margin-right': px, - 'margin-bottom': px, - 'margin-left': px, - 'padding-top': px, - 'padding-right': px, - 'padding-bottom': px, - 'padding-left': px, - 'line-height': isDom, - display: isDom, - clear: isDom - }; -}); - -/** - * Manages context information during a layout. - * - * # Algorithm - * - * This class performs the following jobs: - * - * - Cache DOM reads to avoid reading the same values repeatedly. - * - Buffer DOM writes and flush them as a block to avoid read/write interleaving. - * - Track layout dependencies so each layout does not have to figure out the source of - * its dependent values. - * - Intelligently run layouts when the values on which they depend change (a trigger). - * - Allow layouts to avoid processing when required values are unavailable (a block). - * - * Work done during layout falls into either a "read phase" or a "write phase" and it is - * essential to always be aware of the current phase. Most methods in - * {@link Ext.layout.Layout Layout} are called during a read phase: - * {@link Ext.layout.Layout#calculate calculate}, - * {@link Ext.layout.Layout#completeLayout completeLayout} and - * {@link Ext.layout.Layout#finalizeLayout finalizeLayout}. The exceptions to this are - * {@link Ext.layout.Layout#beginLayout beginLayout}, - * {@link Ext.layout.Layout#beginLayoutCycle beginLayoutCycle} and - * {@link Ext.layout.Layout#finishedLayout finishedLayout} which are called during - * a write phase. While {@link Ext.layout.Layout#finishedLayout finishedLayout} is called - * a write phase, it is really intended to be a catch-all for post-processing after a - * layout run. - * - * In a read phase, it is OK to read the DOM but this should be done using the appropriate - * {@link Ext.layout.ContextItem ContextItem} where possible since that provides a cache - * to avoid redundant reads. No writes should be made to the DOM in a read phase! Instead, - * the values should be written to the proper ContextItem for later write-back. - * - * The rules flip-flop in a write phase. The only difference is that ContextItem methods - * like {@link Ext.layout.ContextItem#getStyle getStyle} will still read the DOM unless the - * value was previously read. This detail is unknowable from the outside of ContextItem, so - * read calls to ContextItem should also be avoided in a write phase. - * - * Calculating interdependent layouts requires a certain amount of iteration. In a given - * cycle, some layouts will contribute results that allow other layouts to proceed. The - * general flow then is to gather all of the layouts (both component and container) in a - * component tree and queue them all for processing. The initial queue order is bottom-up - * and component layout first, then container layout (if applicable) for each component. - * - * This initial step also calls the beginLayout method on all layouts to clear any values - * from the DOM that might interfere with calculations and measurements. In other words, - * this is a "write phase" and reads from the DOM should be strictly avoided. - * - * Next the layout enters into its iterations or "cycles". Each cycle consists of calling - * the {@link Ext.layout.Layout#calculate calculate} method on all layouts in the - * {@link #layoutQueue}. These calls are part of a "read phase" and writes to the DOM should - * be strictly avoided. - * - * # Considerations - * - * **RULE 1**: Respect the read/write cycles. Always use the {@link Ext.layout.ContextItem#getProp getProp} - * or {@link Ext.layout.ContextItem#getDomProp getDomProp} methods to get calculated values; - * only use the {@link Ext.layout.ContextItem#getStyle getStyle} method to read styles; use - * {@link Ext.layout.ContextItem#setProp setProp} to set DOM values. Some reads will, of - * course, still go directly to the DOM, but if there is a method in - * {@link Ext.layout.ContextItem ContextItem} to do a certain job, it should be used instead - * of a lower-level equivalent. - * - * The basic logic flow in {@link Ext.layout.Layout#calculate calculate} consists of gathering - * values by calling {@link Ext.layout.ContextItem#getProp getProp} or - * {@link Ext.layout.ContextItem#getDomProp getDomProp}, calculating results and publishing - * them by calling {@link Ext.layout.ContextItem#setProp setProp}. It is important to realize - * that {@link Ext.layout.ContextItem#getProp getProp} will return `undefined` if the value - * is not yet known. But the act of calling the method is enough to track the fact that the - * calling layout depends (in some way) on this value. In other words, the calling layout is - * "triggered" by the properties it requests. - * - * **RULE 2**: Avoid calling {@link Ext.layout.ContextItem#getProp getProp} unless the value - * is needed. Gratuitous calls cause inefficiency because the layout will appear to depend on - * values that it never actually uses. This applies equally to - * {@link Ext.layout.ContextItem#getDomProp getDomProp} and the test-only methods - * {@link Ext.layout.ContextItem#hasProp hasProp} and {@link Ext.layout.ContextItem#hasDomProp hasDomProp}. - * - * Because {@link Ext.layout.ContextItem#getProp getProp} can return `undefined`, it is often - * the case that subsequent math will produce NaN's. This is usually not a problem as the - * NaN's simply propagate along and result in final results that are NaN. Both `undefined` - * and NaN are ignored by {@link Ext.layout.ContextItem#setProp}, so it is often not necessary - * to even know that this is happening. It does become important for determining if a layout - * is not done or if it might lead to publishing an incorrect (but not NaN or `undefined`) - * value. - * - * **RULE 3**: If a layout has not calculated all the values it is required to calculate, it - * must set {@link Ext.layout.Layout#done done} to `false` before returning from - * {@link Ext.layout.Layout#calculate calculate}. This value is always `true` on entry because - * it is simpler to detect the incomplete state rather than the complete state (especially up - * and down a class hierarchy). - * - * **RULE 4**: A layout must never publish an incomplete (wrong) result. Doing so would cause - * dependent layouts to run their calculations on those wrong values, producing more wrong - * values and some layouts may even incorrectly flag themselves as {@link Ext.layout.Layout#done done} - * before the correct values are determined and republished. Doing this will poison the - * calculations. - * - * **RULE 5**: Each value should only be published by one layout. If multiple layouts attempt - * to publish the same values, it would be nearly impossible to avoid breaking **RULE 4**. To - * help detect this problem, the layout diagnostics will trap on an attempt to set a value - * from different layouts. - * - * Complex layouts can produce many results as part of their calculations. These values are - * important for other layouts to proceed and need to be published by the earliest possible - * call to {@link Ext.layout.Layout#calculate} to avoid unnecessary cycles and poor performance. It is - * also possible, however, for some results to be related in a way such that publishing them - * may be an all-or-none proposition (typically to avoid breaking *RULE 4*). - * - * **RULE 6**: Publish results as soon as they are known to be correct rather than wait for - * all values to be calculated. Waiting for everything to be complete can lead to deadlock. - * The key here is not to forget **RULE 4** in the process. - * - * Some layouts depend on certain critical values as part of their calculations. For example, - * HBox depends on width and cannot do anything until the width is known. In these cases, it - * is best to use {@link Ext.layout.ContextItem#block block} or - * {@link Ext.layout.ContextItem#domBlock domBlock} and thereby avoid processing the layout - * until the needed value is available. - * - * **RULE 7**: Use {@link Ext.layout.ContextItem#block block} or - * {@link Ext.layout.ContextItem#domBlock domBlock} when values are required to make progress. - * This will mimize wasted recalculations. - * - * **RULE 8**: Blocks should only be used when no forward progress can be made. If even one - * value could still be calculated, a block could result in a deadlock. - * - * Historically, layouts have been invoked directly by component code, sometimes in places - * like an `afterLayout` method for a child component. With the flexibility now available - * to solve complex, iterative issues, such things should be done in a responsible layout - * (be it component or container). - * - * **RULE 9**: Use layouts to solve layout issues and don't wait for the layout to finish to - * perform further layouts. This is especially important now that layouts process entire - * component trees and not each layout in isolation. - * - * # Sequence Diagram - * - * The simplest sequence diagram for a layout run looks roughly like this: - * - * Context Layout 1 Item 1 Layout 2 Item 2 - * | | | | | - * ---->X-------------->X | | | - * run X---------------|-----------|---------->X | - * X beginLayout | | | | - * X | | | | - * A X-------------->X | | | - * X calculate X---------->X | | - * X C X getProp | | | - * B X X---------->X | | - * X | setProp | | | - * X | | | | - * D X---------------|-----------|---------->X | - * X calculate | | X---------->X - * X | | | setProp | - * E X | | | | - * X---------------|-----------|---------->X | - * X completeLayout| | F | | - * X | | | | - * G X | | | | - * H X-------------->X | | | - * X calculate X---------->X | | - * X I X getProp | | | - * X X---------->X | | - * X | setProp | | | - * J X-------------->X | | | - * X completeLayout| | | | - * X | | | | - * K X-------------->X | | | - * X---------------|-----------|---------->X | - * X finalizeLayout| | | | - * X | | | | - * L X-------------->X | | | - * X---------------|-----------|---------->X | - * X finishedLayout| | | | - * X | | | | - * M X-------------->X | | | - * X---------------|-----------|---------->X | - * X notifyOwner | | | | - * N | | | | | - * - - - - - - * - * - * Notes: - * - * **A.** This is a call from the {@link #run} method to the {@link #run} method. - * Each layout in the queue will have its {@link Ext.layout.Layout#calculate calculate} - * method called. - * - * **B.** After each {@link Ext.layout.Layout#calculate calculate} method is called the - * {@link Ext.layout.Layout#done done} flag is checked to see if the Layout has completed. - * If it has completed and that layout object implements a - * {@link Ext.layout.Layout#completeLayout completeLayout} method, this layout is queued to - * receive its call. Otherwise, the layout will be queued again unless there are blocks or - * triggers that govern its requeueing. - * - * **C.** The call to {@link Ext.layout.ContextItem#getProp getProp} is made to the Item - * and that will be tracked as a trigger (keyed by the name of the property being requested). - * Changes to this property will cause this layout to be requeued. The call to - * {@link Ext.layout.ContextItem#setProp setProp} will place a value in the item and not - * directly into the DOM. - * - * **D.** Call the other layouts now in the first cycle (repeat **B** and **C** for each - * layout). - * - * **E.** After completing a cycle, if progress was made (new properties were written to - * the context) and if the {@link #layoutQueue} is not empty, the next cycle is run. If no - * progress was made or no layouts are ready to run, all buffered values are written to - * the DOM (a flush). - * - * **F.** After flushing, any layouts that were marked as {@link Ext.layout.Layout#done done} - * that also have a {@link Ext.layout.Layout#completeLayout completeLayout} method are called. - * This can cause them to become no longer done (see {@link #invalidate}). As with - * {@link Ext.layout.Layout#calculate calculate}, this is considered a "read phase" and - * direct DOM writes should be avoided. - * - * **G.** Flushing and calling any pending {@link Ext.layout.Layout#completeLayout completeLayout} - * methods will likely trigger layouts that called {@link Ext.layout.ContextItem#getDomProp getDomProp} - * and unblock layouts that have called {@link Ext.layout.ContextItem#domBlock domBlock}. - * These variants are used when a layout needs the value to be correct in the DOM and not - * simply known. If this does not cause at least one layout to enter the queue, we have a - * layout FAILURE. Otherwise, we continue with the next cycle. - * - * **H.** Call {@link Ext.layout.Layout#calculate calculate} on any layouts in the queue - * at the start of this cycle. Just a repeat of **B** through **G**. - * - * **I.** Once the layout has calculated all that it is resposible for, it can leave itself - * in the {@link Ext.layout.Layout#done done} state. This is the value on entry to - * {@link Ext.layout.Layout#calculate calculate} and must be cleared in that call if the - * layout has more work to do. - * - * **J.** Now that all layouts are done, flush any DOM values and - * {@link Ext.layout.Layout#completeLayout completeLayout} calls. This can again cause - * layouts to become not done, and so we will be back on another cycle if that happens. - * - * **K.** After all layouts are done, call the {@link Ext.layout.Layout#finalizeLayout finalizeLayout} - * method on any layouts that have one. As with {@link Ext.layout.Layout#completeLayout completeLayout}, - * this can cause layouts to become no longer done. This is less desirable than using - * {@link Ext.layout.Layout#completeLayout completeLayout} because it will cause all - * {@link Ext.layout.Layout#finalizeLayout finalizeLayout} methods to be called again - * when we think things are all wrapped up. - * - * **L.** After finishing the last iteration, layouts that have a - * {@link Ext.layout.Layout#finishedLayout finishedLayout} method will be called. This - * call will only happen once per run and cannot cause layouts to be run further. - * - * **M.** After calling finahedLayout, layouts that have a - * {@link Ext.layout.Layout#notifyOwner notifyOwner} method will be called. This - * call will only happen once per run and cannot cause layouts to be run further. - * - * **N.** One last flush to make sure everything has been written to the DOM. - * - * # Inter-Layout Collaboration - * - * Many layout problems require collaboration between multiple layouts. In some cases, this - * is as simple as a component's container layout providing results used by its component - * layout or vise-versa. A slightly more distant collaboration occurs in a box layout when - * stretchmax is used: the child item's component layout provides results that are consumed - * by the ownerCt's box layout to determine the size of the children. - * - * The various forms of interdependence between a container and its children are described by - * each components' {@link Ext.Component#getSizeModel size model}. - * - * To facilitate this collaboration, the following pairs of properties are published to the - * component's {@link Ext.layout.ContextItem ContextItem}: - * - * - width/height: These hold the final size of the component. The layout indicated by the - * {@link Ext.Component#getSizeModel size model} is responsible for setting these. - * - contentWidth/contentHeight: These hold size information published by the container - * layout or from DOM measurement. These describe the content only. These values are - * used by the component layout to determine the outer width/height when that component - * is {@link Ext.Component#shrinkWrap shrink-wrapped}. They are also used to - * determine overflow. All container layouts must publish these values for dimensions - * that are shrink-wrapped. If a component has raw content (not container items), the - * componentLayout must publish these values instead. - * - * @private - */ -Ext.define('Ext.layout.Context', { - requires: [ - 'Ext.perf.Monitor', - 'Ext.util.Queue', - 'Ext.layout.ContextItem', - 'Ext.layout.Layout', - 'Ext.fx.Anim', - 'Ext.fx.Manager' - ], - remainingLayouts: 0, - /** - * @property {Number} state One of these values: - * - * - 0 - Before run - * - 1 - Running - * - 2 - Run complete - */ - state: 0, - /** - * @property {Number} cycleWatchDog - * This value is used to detect layouts that cannot progress by checking the amount of - * cycles processed. The value should be large enough to satisfy all but exceptionally large - * layout structures. When the amount of cycles is reached, the layout will fail. This should - * only be used for debugging, layout failures should be considered as an exceptional occurrence. - * @private - * @since 5.1.1 - */ - cycleWatchDog: 200, - constructor: function(config) { - var me = this; - Ext.apply(me, config); - // holds the ContextItem collection, keyed by element id - me.items = {}; - // a collection of layouts keyed by layout id - me.layouts = {}; - // the number of blocks of any kind: - me.blockCount = 0; - // the number of cycles that have been run: - me.cycleCount = 0; - // the number of flushes to the DOM: - me.flushCount = 0; - // the number of layout calculate calls: - me.calcCount = 0; - me.animateQueue = me.newQueue(); - me.completionQueue = me.newQueue(); - me.finalizeQueue = me.newQueue(); - me.finishQueue = me.newQueue(); - me.flushQueue = me.newQueue(); - me.invalidateData = {}; - /** - * @property {Ext.util.Queue} layoutQueue - * List of layouts to perform. - */ - me.layoutQueue = me.newQueue(); - // this collection is special because we ensure that there are no parent/child pairs - // present, only distinct top-level components - me.invalidQueue = []; - me.triggers = { - data: {}, - /* - layoutId: [ - { item: contextItem, prop: propertyName } - ] - */ - dom: {} - }; - }, - callLayout: function(layout, methodName) { - this.currentLayout = layout; - layout[methodName](this.getCmp(layout.owner)); - }, - cancelComponent: function(comp, isChild, isDestroying) { - var me = this, - components = comp, - isArray = !comp.isComponent, - length = isArray ? components.length : 1, - i, k, klen, items, layout, newQueue, oldQueue, entry, temp, ownerCtContext; - for (i = 0; i < length; ++i) { - if (isArray) { - comp = components[i]; - } - if (isDestroying) { - if (comp.ownerCt) { - // If the component is being destroyed, remove the component's ContextItem from its parent's contextItem.childItems array - ownerCtContext = this.items[comp.ownerCt.el.id]; - if (ownerCtContext) { - Ext.Array.remove(ownerCtContext.childItems, me.getCmp(comp)); - } - } else if (comp.rendered) { - me.removeEl(comp.el); - } - } - if (!isChild) { - oldQueue = me.invalidQueue; - klen = oldQueue.length; - if (klen) { - me.invalidQueue = newQueue = []; - for (k = 0; k < klen; ++k) { - entry = oldQueue[k]; - temp = entry.item.target; - if (temp !== comp && !temp.up(comp)) { - newQueue.push(entry); - } - } - } - } - layout = comp.componentLayout; - me.cancelLayout(layout); - if (layout.getLayoutItems) { - items = layout.getLayoutItems(); - if (items.length) { - me.cancelComponent(items, true); - } - } - if (comp.isContainer && !comp.collapsed) { - layout = comp.layout; - me.cancelLayout(layout); - items = layout.getVisibleItems(); - if (items.length) { - me.cancelComponent(items, true); - } - } - } - }, - cancelLayout: function(layout) { - var me = this; - me.completionQueue.remove(layout); - me.finalizeQueue.remove(layout); - me.finishQueue.remove(layout); - me.layoutQueue.remove(layout); - if (layout.running) { - me.layoutDone(layout); - } - layout.ownerContext = null; - }, - clearTriggers: function(layout, inDom) { - var id = layout.id, - collection = this.triggers[inDom ? 'dom' : 'data'], - triggers = collection && collection[id], - length = (triggers && triggers.length) || 0, - i, item, trigger; - for (i = 0; i < length; ++i) { - trigger = triggers[i]; - item = trigger.item; - collection = inDom ? item.domTriggers : item.triggers; - delete collection[trigger.prop][id]; - } - }, - /** - * Flushes any pending writes to the DOM by calling each ContextItem in the flushQueue. - */ - flush: function() { - var me = this, - items = me.flushQueue.clear(), - length = items.length, - i; - if (length) { - ++me.flushCount; - for (i = 0; i < length; ++i) { - items[i].flush(); - } - } - }, - flushAnimations: function() { - var me = this, - items = me.animateQueue.clear(), - len = items.length, - i; - if (len) { - for (i = 0; i < len; i++) { - // Each Component may refuse to participate in animations. - // This is used by the BoxReorder plugin which drags a Component, - // during which that Component must be exempted from layout positioning. - if (items[i].target.animate !== false) { - items[i].flushAnimations(); - } - } - // Ensure the first frame fires now to avoid a browser repaint with the elements in the "to" state - // before they are returned to their "from" state by the animation. - Ext.fx.Manager.runner(); - } - }, - flushInvalidates: function() { - var me = this, - queue = me.invalidQueue, - length = queue && queue.length, - comp, components, entry, i; - me.invalidQueue = []; - if (length) { - components = []; - for (i = 0; i < length; ++i) { - comp = (entry = queue[i]).item.target; - // we filter out-of-body components here but allow them into the queue to - // ensure that their child components are coalesced out (w/no additional - // cost beyond our normal effort to avoid parent/child components in the - // queue) - if (!comp.container.isDetachedBody) { - components.push(comp); - if (entry.options) { - me.invalidateData[comp.id] = entry.options; - } - } - } - me.invalidate(components, null); - } - }, - flushLayouts: function(queueName, methodName, dontClear) { - var me = this, - layouts = dontClear ? me[queueName].items : me[queueName].clear(), - length = layouts.length, - i, layout; - if (length) { - for (i = 0; i < length; ++i) { - layout = layouts[i]; - if (!layout.running) { - me.callLayout(layout, methodName); - } - } - me.currentLayout = null; - } - }, - /** - * Returns the ContextItem for a component. - * @param {Ext.Component} cmp - */ - getCmp: function(cmp) { - return this.getItem(cmp, cmp.el); - }, - /** - * Returns the ContextItem for an element. - * @param {Ext.layout.ContextItem} parent - * @param {Ext.dom.Element} el - */ - getEl: function(parent, el) { - var item = this.getItem(el, el); - if (!item.parent) { - item.parent = parent; - // all items share an empty children array (to avoid null checks), so we can - // only push on to the children array if there is already something there (we - // copy-on-write): - if (parent.children.length) { - parent.children.push(item); - } else { - parent.children = [ - item - ]; - } - } - // now parent has its own children[] (length=1) - return item; - }, - getItem: function(target, el) { - var id = el.id, - items = this.items, - item = items[id] || (items[id] = new Ext.layout.ContextItem({ - context: this, - target: target, - el: el - })); - return item; - }, - handleFailure: function() { - // This method should never be called, but is need when layouts fail (hence the - // "should never"). We just disconnect any of the layouts from the run and return - // them to the state they would be in had the layout completed properly. - var layouts = this.layouts, - layout, key; - Ext.failedLayouts = (Ext.failedLayouts || 0) + 1; - for (key in layouts) { - layout = layouts[key]; - if (layouts.hasOwnProperty(key)) { - layout.running = false; - layout.ownerContext = null; - } - } - if (Ext.devMode === 2 && !this.pageAnalyzerMode) { - Ext.raise('Layout run failed'); - } else { - Ext.log.error('Layout run failed'); - } - }, - /** - * Invalidates one or more components' layouts (component and container). This can be - * called before run to identify the components that need layout or during the run to - * restart the layout of a component. This is called internally to flush any queued - * invalidations at the start of a cycle. If called during a run, it is not expected - * that new components will be introduced to the layout. - * - * @param {Ext.Component/Array} components An array of Components or a single Component. - * @param {Boolean} full True if all properties should be invalidated, otherwise only - * those calculated by the component should be invalidated. - */ - invalidate: function(components, full) { - var me = this, - isArray = !components.isComponent, - containerLayoutDone, ownerLayout, firstTime, i, comp, item, items, length, componentLayout, layout, invalidateOptions, token, skipLayout; - for (i = 0 , length = isArray ? components.length : 1; i < length; ++i) { - comp = isArray ? components[i] : components; - if (comp.rendered && !comp.hidden) { - ownerLayout = comp.ownerLayout; - componentLayout = comp.componentLayout; - skipLayout = false; - if ((!ownerLayout || !ownerLayout.needsItemSize) && comp.liquidLayout) { - // our owning layout doesn't need us to run, and our componentLayout - // wants to opt out because it uses liquid CSS layout. - // We can skip invalidation for this component. - skipLayout = true; - } - // if we are skipping layout, we can also skip creation of the context - // item, unless our owner layout needs it to set our size. - if (!skipLayout || (ownerLayout && ownerLayout.setsItemSize)) { - item = me.getCmp(comp); - firstTime = !item.state; - // If the component has had no changes which necessitate a layout, do not lay it out. - // Temporarily disabled because this breaks dock layout (see EXTJSIV-10251) - // if (item.optOut) { - // skipLayout = true; - // } - layout = (comp.isContainer && !comp.collapsed) ? comp.layout : null; - // Extract any invalidate() options for this item. - invalidateOptions = me.invalidateData[item.id]; - delete me.invalidateData[item.id]; - // We invalidate the contextItem's in a top-down manner so that SizeModel - // info for containers is available to their children. This is a critical - // optimization since sizeModel determination often requires knowing the - // sizeModel of the ownerCt. If this weren't cached as we descend, this - // would be an O(N^2) operation! (where N=number of components, or 300+/- - // in Themes) - token = item.init(full, invalidateOptions); - } - if (skipLayout) { - - continue; - } - if (invalidateOptions) { - me.processInvalidate(invalidateOptions, item, 'before'); - } - // Allow the component layout a chance to effect its size model before we - // recurse down the component hierarchy (since children need to know the - // size model of their ownerCt). - if (componentLayout.beforeLayoutCycle) { - componentLayout.beforeLayoutCycle(item); - } - if (layout && layout.beforeLayoutCycle) { - // allow the container layout take a peek as well. Table layout can - // influence its children's styling due to the interaction of nesting - // table-layout:fixed and auto inside each other without intervening - // elements of known size. - layout.beforeLayoutCycle(item); - } - // Finish up the item-level processing that is based on the size model of - // the component. - token = item.initContinue(token); - // Start this state variable at true, since that is the value we want if - // they do not apply (i.e., no work of this kind on which to wait). - containerLayoutDone = true; - // A ComponentLayout MUST implement getLayoutItems to allow its children - // to be collected. Ext.container.Container does this, but non-Container - // Components which manage Components as part of their structure (e.g., - // HtmlEditor) must still return child Components via getLayoutItems. - if (componentLayout.getLayoutItems) { - componentLayout.renderChildren(); - items = componentLayout.getLayoutItems(); - if (items.length) { - me.invalidate(items, true); - } - } - if (layout) { - containerLayoutDone = false; - layout.renderChildren(); - if (layout.needsItemSize || layout.activeItemCount) { - // if the layout specified that it needs the layouts of its children - // to run, or if the number of "liquid" child layouts is greater - // than 0, we need to recurse into the children, since some or - // all of them may need their layouts to run. - items = layout.getVisibleItems(); - if (items.length) { - me.invalidate(items, true); - } - } - } - // Finish the processing that requires the size models of child items to - // be determined (and some misc other stuff). - item.initDone(containerLayoutDone); - // Inform the layouts that we are about to begin (or begin again) now that - // the size models of the component and its children are setup. - me.resetLayout(componentLayout, item, firstTime); - if (layout) { - me.resetLayout(layout, item, firstTime); - } - // This has to occur after the component layout has had a chance to begin - // so that we can determine what kind of animation might be needed. TODO- - // move this determination into the layout itself. - item.initAnimation(); - if (invalidateOptions) { - me.processInvalidate(invalidateOptions, item, 'after'); - } - } - } - me.currentLayout = null; - }, - // Returns true is descendant is a descendant of ancestor - isDescendant: function(ancestor, descendant) { - if (ancestor.isContainer) { - for (var c = descendant.ownerCt; c; c = c.ownerCt) { - if (c === ancestor) { - return true; - } - } - } - return false; - }, - layoutDone: function(layout) { - var ownerContext = layout.ownerContext; - layout.running = false; - // Once a component layout completes, we can mark it as "done". - if (layout.isComponentLayout) { - if (ownerContext.measuresBox) { - ownerContext.onBoxMeasured(); - } - // be sure to release our boxParent - ownerContext.setProp('done', true); - } else { - ownerContext.setProp('containerLayoutDone', true); - } - --this.remainingLayouts; - ++this.progressCount; - }, - // a layout completion is progress - newQueue: function() { - return new Ext.util.Queue(); - }, - processInvalidate: function(options, item, name) { - // When calling a callback, the currentLayout needs to be adjusted so - // that whichever layout caused the invalidate is the currentLayout... - if (options[name]) { - var me = this, - currentLayout = me.currentLayout; - me.currentLayout = options.layout || null; - options[name](item, options); - me.currentLayout = currentLayout; - } - }, - /** - * Queues a ContextItem to have its {@link Ext.layout.ContextItem#flushAnimations} method called. - * - * @param {Ext.layout.ContextItem} item - * @private - */ - queueAnimation: function(item) { - this.animateQueue.add(item); - }, - /** - * Queues a layout to have its {@link Ext.layout.Layout#completeLayout} method called. - * - * @param {Ext.layout.Layout} layout - * @private - */ - queueCompletion: function(layout) { - this.completionQueue.add(layout); - }, - /** - * Queues a layout to have its {@link Ext.layout.Layout#finalizeLayout} method called. - * - * @param {Ext.layout.Layout} layout - * @private - */ - queueFinalize: function(layout) { - this.finalizeQueue.add(layout); - }, - /** - * Queues a ContextItem for the next flush to the DOM. This should only be called by - * the {@link Ext.layout.ContextItem} class. - * - * @param {Ext.layout.ContextItem} item - * @private - */ - queueFlush: function(item) { - this.flushQueue.add(item); - }, - chainFns: function(oldOptions, newOptions, funcName) { - var me = this, - oldLayout = oldOptions.layout, - newLayout = newOptions.layout, - oldFn = oldOptions[funcName], - newFn = newOptions[funcName]; - // Call newFn last so it can get the final word on things... also, the "this" - // pointer will be passed correctly by createSequence with oldFn first. - return function(contextItem) { - var prev = me.currentLayout; - if (oldFn) { - me.currentLayout = oldLayout; - oldFn.call(oldOptions.scope || oldOptions, contextItem, oldOptions); - } - me.currentLayout = newLayout; - newFn.call(newOptions.scope || newOptions, contextItem, newOptions); - me.currentLayout = prev; - }; - }, - purgeInvalidates: function() { - var me = this, - newQueue = [], - oldQueue = me.invalidQueue, - oldLength = oldQueue.length, - oldIndex, newIndex, newEntry, newComp, oldEntry, oldComp, keep; - for (oldIndex = 0; oldIndex < oldLength; ++oldIndex) { - oldEntry = oldQueue[oldIndex]; - oldComp = oldEntry.item.target; - keep = true; - for (newIndex = newQueue.length; newIndex--; ) { - newEntry = newQueue[newIndex]; - newComp = newEntry.item.target; - if (oldComp.isLayoutChild(newComp)) { - keep = false; - break; - } - if (newComp.isLayoutChild(oldComp)) { - Ext.Array.erase(newQueue, newIndex, 1); - } - } - if (keep) { - newQueue.push(oldEntry); - } - } - me.invalidQueue = newQueue; - }, - /** - * Queue a component (and its tree) to be invalidated on the next cycle. - * - * @param {Ext.Component/Ext.layout.ContextItem} item The component or ContextItem to invalidate. - * @param {Object} options An object describing how to handle the invalidation (see - * {@link Ext.layout.ContextItem#invalidate} for details). - * @private - */ - queueInvalidate: function(item, options) { - var me = this, - newQueue = [], - oldQueue = me.invalidQueue, - index = oldQueue.length, - comp, old, oldComp, oldOptions, oldState; - if (item.isComponent) { - comp = item; - item = me.items[comp.el.id]; - if (item) { - item.recalculateSizeModel(); - } else { - item = me.getCmp(comp); - } - } else { - comp = item.target; - } - item.invalid = true; - // See if comp is contained by any component already in the queue (ignore comp if - // that is the case). Eliminate any components in the queue that are contained by - // comp (by not adding them to newQueue). - while (index--) { - old = oldQueue[index]; - oldComp = old.item.target; - if (!comp.isFloating && comp.up(oldComp)) { - return; - } - // oldComp contains comp, so this invalidate is redundant - if (oldComp === comp) { - // if already in the queue, update the options... - if (!(oldOptions = old.options)) { - old.options = options; - } else if (options) { - if (options.widthModel) { - oldOptions.widthModel = options.widthModel; - } - if (options.heightModel) { - oldOptions.heightModel = options.heightModel; - } - if (!(oldState = oldOptions.state)) { - oldOptions.state = options.state; - } else if (options.state) { - Ext.apply(oldState, options.state); - } - if (options.before) { - oldOptions.before = me.chainFns(oldOptions, options, 'before'); - } - if (options.after) { - oldOptions.after = me.chainFns(oldOptions, options, 'after'); - } - } - // leave the old queue alone now that we've update this comp's entry... - return; - } - if (!oldComp.isLayoutChild(comp)) { - newQueue.push(old); - } - } - // comp does not contain oldComp - // else if (oldComp isDescendant of comp) skip - // newQueue contains only those components not a descendant of comp - // to get here, comp must not be a child of anything already in the queue, so it - // needs to be added along with its "options": - newQueue.push({ - item: item, - options: options - }); - me.invalidQueue = newQueue; - }, - queueItemLayouts: function(item) { - var comp = item.isComponent ? item : item.target, - layout = comp.componentLayout; - if (!layout.pending && !layout.invalid && !layout.done) { - this.queueLayout(layout); - } - layout = comp.layout; - if (layout && !layout.pending && !layout.invalid && !layout.done && !comp.collapsed) { - this.queueLayout(layout); - } - }, - /** - * Queues a layout for the next calculation cycle. This should not be called if the - * layout is done, blocked or already in the queue. The only classes that should call - * this method are this class and {@link Ext.layout.ContextItem}. - * - * @param {Ext.layout.Layout} layout The layout to add to the queue. - * @private - */ - queueLayout: function(layout) { - this.layoutQueue.add(layout); - layout.pending = true; - }, - /** - * Removes the ContextItem for an element from the cache and from the parent's - * "children" array. - * @param {Ext.dom.Element} el - * @param {Ext.layout.ContextItem} parent - */ - removeEl: function(el, parent) { - var id = el.id, - children = parent ? parent.children : null, - items = this.items; - if (children) { - Ext.Array.remove(children, items[id]); - } - delete items[id]; - }, - /** - * Resets the given layout object. This is called at the start of the run and can also - * be called during the run by calling {@link #invalidate}. - */ - resetLayout: function(layout, ownerContext, firstTime) { - var me = this; - me.currentLayout = layout; - layout.done = false; - layout.pending = true; - layout.firedTriggers = 0; - me.layoutQueue.add(layout); - if (firstTime) { - me.layouts[layout.id] = layout; - // track the layout for this run by its id - layout.running = true; - if (layout.finishedLayout) { - me.finishQueue.add(layout); - } - // reset or update per-run counters: - ++me.remainingLayouts; - ++layout.layoutCount; - // the number of whole layouts run for the layout - layout.ownerContext = ownerContext; - layout.beginCount = 0; - // the number of beginLayout calls - layout.blockCount = 0; - // the number of blocks set for the layout - layout.calcCount = 0; - // the number of times calculate is called - layout.triggerCount = 0; - // the number of triggers set for the layout - if (!layout.initialized) { - layout.initLayout(); - } - layout.beginLayout(ownerContext); - } else { - ++layout.beginCount; - if (!layout.running) { - // back into the mahem with this one: - ++me.remainingLayouts; - layout.running = true; - layout.ownerContext = ownerContext; - if (layout.isComponentLayout) { - // this one is fun... if we call setProp('done', false) that would still - // trigger/unblock layouts, but what layouts are really looking for with - // this property is for it to go to true, not just be set to a value... - ownerContext.unsetProp('done'); - } - // and it needs to be removed from the completion and/or finalize queues... - me.completionQueue.remove(layout); - me.finalizeQueue.remove(layout); - } - } - layout.beginLayoutCycle(ownerContext, firstTime); - }, - /** - * Runs the layout calculations. This can be called only once on this object. - * @return {Boolean} True if all layouts were completed, false if not. - */ - run: function() { - var me = this, - flushed = false, - watchDog = me.cycleWatchDog; - me.purgeInvalidates(); - me.flushInvalidates(); - me.state = 1; - me.totalCount = me.layoutQueue.getCount(); - // We may start with unflushed data placed by beginLayout calls. Since layouts may - // use setProp as a convenience, even in a write phase, we don't want to transition - // to a read phase with unflushed data since we can write it now "cheaply". Also, - // these value could easily be needed in the DOM in order to really get going with - // the calculations. In particular, fixed (configured) dimensions fall into this - // category. - me.flush(); - // While we have layouts that have not completed... - while ((me.remainingLayouts || me.invalidQueue.length) && watchDog--) { - if (me.invalidQueue.length) { - me.flushInvalidates(); - } - // if any of them can run right now, run them - if (me.runCycle()) { - flushed = false; - } - // progress means we probably need to flush something - // but not all progress appears in the flushQueue (e.g. 'contentHeight') - else if (!flushed) { - // as long as we are making progress, flush updates to the DOM and see if - // that triggers or unblocks any layouts... - me.flush(); - flushed = true; - // all flushed now, so more progress is required - me.flushLayouts('completionQueue', 'completeLayout'); - } else if (!me.invalidQueue.length) { - // after a flush, we must make progress or something is WRONG - me.state = 2; - break; - } - if (!(me.remainingLayouts || me.invalidQueue.length)) { - me.flush(); - me.flushLayouts('completionQueue', 'completeLayout'); - me.flushLayouts('finalizeQueue', 'finalizeLayout'); - } - } - return me.runComplete(); - }, - runComplete: function() { - var me = this; - me.state = 2; - if (me.remainingLayouts) { - me.handleFailure(); - return false; - } - me.flush(); - // Call finishedLayout on all layouts, but do not clear the queue. - me.flushLayouts('finishQueue', 'finishedLayout', true); - // Call notifyOwner on all layouts and then clear the queue. - me.flushLayouts('finishQueue', 'notifyOwner'); - me.flush(); - // in case any setProp calls were made - me.flushAnimations(); - return true; - }, - /** - * Performs one layout cycle by calling each layout in the layout queue. - * @return {Boolean} True if some progress was made, false if not. - * @protected - */ - runCycle: function() { - var me = this, - layouts = me.layoutQueue.clear(), - length = layouts.length, - i; - ++me.cycleCount; - // This value is incremented by ContextItem#setProp whenever new values are set - // (thereby detecting forward progress): - me.progressCount = 0; - for (i = 0; i < length; ++i) { - me.runLayout(me.currentLayout = layouts[i]); - } - me.currentLayout = null; - return me.progressCount > 0; - }, - /** - * Runs one layout as part of a cycle. - * @private - */ - runLayout: function(layout) { - var me = this, - ownerContext = me.getCmp(layout.owner); - layout.pending = false; - if (ownerContext.state.blocks) { - return; - } - // We start with the assumption that the layout will finish and if it does not, it - // must clear this flag. It turns out this is much simpler than knowing when a layout - // is done (100% correctly) when base classes and derived classes are collaborating. - // Knowing that some part of the layout is not done is much more obvious. - layout.done = true; - ++layout.calcCount; - ++me.calcCount; - layout.calculate(ownerContext); - if (layout.done) { - me.layoutDone(layout); - if (layout.completeLayout) { - me.queueCompletion(layout); - } - if (layout.finalizeLayout) { - me.queueFinalize(layout); - } - } else if (!layout.pending && !layout.invalid && !(layout.blockCount + layout.triggerCount - layout.firedTriggers)) { - // jshint ignore:line - // A layout that is not done and has no blocks or triggers that will queue it - // automatically, must be queued now: - me.queueLayout(layout); - } - }, - /** - * Set the size of a component, element or composite or an array of components or elements. - * @param {Ext.Component/Ext.Component[]/Ext.dom.Element/Ext.dom.Element[]/Ext.dom.CompositeElement} item - * The item(s) to size. - * @param {Number} width The new width to set (ignored if undefined or NaN). - * @param {Number} height The new height to set (ignored if undefined or NaN). - */ - setItemSize: function(item, width, height) { - var items = item, - len = 1, - contextItem, i; - // NOTE: we don't pre-check for validity because: - // - maybe only one dimension is valid - // - the diagnostics layer will track the setProp call to help find who is trying - // (but failing) to set a property - // - setProp already checks this anyway - if (item.isComposite) { - items = item.elements; - len = items.length; - item = items[0]; - } else if (!item.dom && !item.el) { - // array by process of elimination - len = items.length; - item = items[0]; - } - // else len = 1 and items = item (to avoid error on "items[++i]") - for (i = 0; i < len; ) { - contextItem = this.get(item); - contextItem.setSize(width, height); - item = items[++i]; - } - }, - // this accomodation avoids making an array of 1 - //------------------------------------------------------------------------- - // Diagnostics - debugHooks: { - $enabled: false, - // off by default - pageAnalyzerMode: true, - logOn: { - //boxParent: true, - //calculate: true, - //cancelComponent: true, - //cancelLayout: true, - //doInvalidate: true, - //flush: true, - //flushInvalidate: true, - //invalidate: true, - //initItem: true, - //layoutDone: true, - //queueLayout: true, - //resetLayout: true, - //runCycle: true, - //setProp: true, - 0: 0 - }, - //profileLayoutsByType: true, - //reportOnSuccess: true, - cancelComponent: function(comp) { - if (this.logOn.cancelComponent) { - Ext.log('cancelCmp: ', comp.id); - } - this.callParent(arguments); - }, - cancelLayout: function(layout) { - if (this.logOn.cancelLayout) { - Ext.log('cancelLayout: ', this.getLayoutName(layout)); - } - this.callParent(arguments); - }, - callLayout: function(layout, methodName) { - var accum = this.accumByType[layout.type], - frame = accum && accum.enter(); - this.callParent(arguments); - if (accum) { - frame.leave(); - } - }, - checkRemainingLayouts: function() { - var me = this, - expected = 0, - key, layout; - for (key in me.layouts) { - layout = me.layouts[key]; - if (me.layouts.hasOwnProperty(key) && layout.running) { - ++expected; - } - } - if (me.remainingLayouts !== expected) { - Ext.raise({ - msg: 'Bookkeeping error me.remainingLayouts' - }); - } - }, - flush: function() { - if (this.logOn.flush) { - var items = this.flushQueue; - Ext.log('--- Flush ', items && items.getCount()); - } - return this.callParent(arguments); - }, - flushInvalidates: function() { - if (this.logOn.flushInvalidate) { - Ext.log('>> flushInvalidates'); - } - var ret = this.callParent(arguments); - if (this.logOn.flushInvalidate) { - Ext.log('<< flushInvalidates'); - } - return ret; - }, - getCmp: function(target) { - var ret = this.callParent(arguments); - if (!ret.wrapsComponent) { - Ext.raise({ - msg: target.id + ' is not a component' - }); - } - return ret; - }, - getEl: function(parent, target) { - var ret = this.callParent(arguments); - if (ret && ret.wrapsComponent) { - Ext.raise({ - msg: parent.id + '/' + target.id + ' is a component (expected element)' - }); - } - return ret; - }, - getLayoutName: function(layout) { - return layout.owner.id + '<' + layout.type + '>'; - }, - layoutDone: function(layout) { - var me = this, - name = me.getLayoutName(layout); - if (me.logOn.layoutDone) { - Ext.log('layoutDone: ', name, ' ( ', me.remainingLayouts, ' running)'); - } - if (!layout.running) { - Ext.raise({ - msg: name + ' is already done' - }); - } - if (!me.remainingLayouts) { - Ext.raise({ - msg: name + ' finished but no layouts are running' - }); - } - me.callParent(arguments); - }, - layoutTreeHasFailures: function(layout, reported) { - var me = this; - function hasFailure(lo) { - var failure = !lo.done, - key, childLayout; - if (lo.done) { - for (key in me.layouts) { - if (me.layouts.hasOwnProperty(key)) { - childLayout = me.layouts[key]; - if (childLayout.owner.ownerLayout === lo) { - if (hasFailure(childLayout)) { - failure = true; - } - } - } - } - } - return failure; - } - if (hasFailure(layout)) { - return true; - } - function markReported(lo) { - var key, childLayout; - reported[lo.id] = 1; - for (key in me.layouts) { - if (me.layouts.hasOwnProperty(key)) { - childLayout = me.layouts[key]; - if (childLayout.owner.ownerLayout === lo) { - markReported(childLayout); - } - } - } - } - markReported(layout); - return false; - }, - queueLayout: function(layout) { - if (layout.done || layout.blockCount || layout.pending) { - Ext.raise({ - msg: this.getLayoutName(layout) + ' should not be queued for layout' - }); - } - if (this.logOn.queueLayout) { - Ext.log('Queue ', this.getLayoutName(layout)); - } - return this.callParent(arguments); - }, - reportLayoutResult: function(layout, reported) { - var me = this, - owner = layout.owner, - ownerContext = me.getCmp(owner), - blockedBy = [], - triggeredBy = [], - key, value, i, length, childLayout, item, setBy, info; - reported[layout.id] = 1; - for (key in layout.blockedBy) { - if (layout.blockedBy.hasOwnProperty(key)) { - blockedBy.push(layout.blockedBy[key]); - } - } - blockedBy.sort(); - for (key in me.triggersByLayoutId[layout.id]) { - if (me.triggersByLayoutId[layout.id].hasOwnProperty(key)) { - value = me.triggersByLayoutId[layout.id][key]; - triggeredBy.push({ - name: key, - info: value - }); - } - } - triggeredBy.sort(function(a, b) { - return a.name < b.name ? -1 : (b.name < a.name ? 1 : 0); - }); - Ext.log({ - indent: 1 - }, (layout.done ? '++' : '--'), me.getLayoutName(layout), (ownerContext.isBoxParent ? ' [isBoxParent]' : ''), (ownerContext.boxChildren ? ' - boxChildren: ' + ownerContext.state.boxesMeasured + '/' + ownerContext.boxChildren.length : ''), ownerContext.boxParent ? (' - boxParent: ' + ownerContext.boxParent.id) : '', ' - size: ', ownerContext.widthModel.name, '/', ownerContext.heightModel.name); - if (!layout.done || me.reportOnSuccess) { - if (blockedBy.length) { - ++Ext.log.indent; - Ext.log({ - indent: 1 - }, 'blockedBy: count=', layout.blockCount); - length = blockedBy.length; - for (i = 0; i < length; i++) { - Ext.log(blockedBy[i]); - } - Ext.log.indent -= 2; - } - if (triggeredBy.length) { - ++Ext.log.indent; - Ext.log({ - indent: 1 - }, 'triggeredBy: count=' + layout.triggerCount); - length = triggeredBy.length; - for (i = 0; i < length; i++) { - info = value.info || value; - item = info.item; - setBy = (item.setBy && item.setBy[info.name]) || '?'; - value = triggeredBy[i]; - Ext.log(value.name, ' (', item.props[info.name], ') dirty: ', (item.dirty ? !!item.dirty[info.name] : false), ', setBy: ', setBy); - } - Ext.log.indent -= 2; - } - } - for (key in me.layouts) { - if (me.layouts.hasOwnProperty(key)) { - childLayout = me.layouts[key]; - if (!childLayout.done && childLayout.owner.ownerLayout === layout) { - me.reportLayoutResult(childLayout, reported); - } - } - } - for (key in me.layouts) { - if (me.layouts.hasOwnProperty(key)) { - childLayout = me.layouts[key]; - if (childLayout.done && childLayout.owner.ownerLayout === layout) { - me.reportLayoutResult(childLayout, reported); - } - } - } - --Ext.log.indent; - }, - resetLayout: function(layout) { - var me = this, - type = layout.type, - name = me.getLayoutName(layout), - accum = me.accumByType[type], - frame; - if (me.logOn.resetLayout) { - Ext.log('resetLayout: ', name, ' ( ', me.remainingLayouts, ' running)'); - } - if (!me.state) { - // if (first time ... before run) - if (!accum && me.profileLayoutsByType) { - me.accumByType[type] = accum = Ext.Perf.get('layout_' + layout.type); - } - me.numByType[type] = (me.numByType[type] || 0) + 1; - } - frame = accum && accum.enter(); - me.callParent(arguments); - if (accum) { - frame.leave(); - } - me.checkRemainingLayouts(); - }, - round: function(t) { - return Math.round(t * 1000) / 1000; - }, - run: function() { - var me = this, - ret, time, key, i, layout, boxParent, children, n, reported, unreported, calcs, total, calcsLength, calc; - me.accumByType = {}; - me.calcsByType = {}; - me.numByType = {}; - me.timesByType = {}; - me.triggersByLayoutId = {}; - Ext.log.indentSize = 3; - Ext.log('==================== LAYOUT ===================='); - time = Ext.perf.getTimestamp(); - ret = me.callParent(arguments); - time = Ext.perf.getTimestamp() - time; - if (me.logOn.boxParent && me.boxParents) { - for (key in me.boxParents) { - if (me.boxParents.hasOwnProperty(key)) { - boxParent = me.boxParents[key]; - children = boxParent.boxChildren; - n = children.length; - Ext.log('boxParent: ', boxParent.id); - for (i = 0; i < n; ++i) { - Ext.log(' --> ', children[i].id); - } - } - } - } - if (ret) { - Ext.log('----------------- SUCCESS -----------------'); - } else { - Ext.log({ - level: 'error' - }, '----------------- FAILURE -----------------'); - } - for (key in me.layouts) { - if (me.layouts.hasOwnProperty(key)) { - layout = me.layouts[key]; - if (layout.running) { - Ext.log.error('Layout left running: ', me.getLayoutName(layout)); - } - if (layout.ownerContext) { - Ext.log.error('Layout left connected: ', me.getLayoutName(layout)); - } - } - } - if (!ret || me.reportOnSuccess) { - reported = {}; - unreported = 0; - for (key in me.layouts) { - if (me.layouts.hasOwnProperty(key)) { - layout = me.layouts[key]; - if (me.items[layout.owner.el.id].isTopLevel) { - if (me.reportOnSuccess || me.layoutTreeHasFailures(layout, reported)) { - me.reportLayoutResult(layout, reported); - } - } - } - } - // Just in case we missed any layouts... - for (key in me.layouts) { - if (me.layouts.hasOwnProperty(key)) { - layout = me.layouts[key]; - if (!reported[layout.id]) { - if (!unreported) { - Ext.log('----- Unreported!! -----'); - } - ++unreported; - me.reportLayoutResult(layout, reported); - } - } - } - } - Ext.log('Cycles: ', me.cycleCount, ', Flushes: ', me.flushCount, ', Calculates: ', me.calcCount, ' in ', me.round(time), ' msec'); - Ext.log('Calculates by type:'); - /*Ext.Object.each(me.numByType, function (type, total) { - Ext.log(type, ': ', total, ' in ', me.calcsByType[type], ' tries (', - Math.round(me.calcsByType[type] / total * 10) / 10, 'x) at ', - me.round(me.timesByType[type]), ' msec (avg ', - me.round(me.timesByType[type] / me.calcsByType[type]), ' msec)'); - });*/ - calcs = []; - for (key in me.numByType) { - if (me.numByType.hasOwnProperty(key)) { - total = me.numByType[key]; - calcs.push({ - type: key, - total: total, - calcs: me.calcsByType[key], - multiple: Math.round(me.calcsByType[key] / total * 10) / 10, - calcTime: me.round(me.timesByType[key]), - avgCalcTime: me.round(me.timesByType[key] / me.calcsByType[key]) - }); - } - } - calcs.sort(function(a, b) { - return b.calcTime - a.calcTime; - }); - calcsLength = calcs.length; - for (i = 0; i < calcsLength; i++) { - calc = calcs[i]; - Ext.log(calc.type, ': ', calc.total, ' in ', calc.calcs, ' tries (', calc.multiple, 'x) at ', calc.calcTime, ' msec (avg ', calc.avgCalcTime, ' msec)'); - } - return ret; - }, - runCycle: function() { - if (this.logOn.runCycle) { - Ext.log('>>> Cycle ', this.cycleCount, ' (queue length: ', this.layoutQueue.length, ')'); - } - return this.callParent(arguments); - }, - runLayout: function(layout) { - var me = this, - type = layout.type, - accum = me.accumByType[type], - frame, ret, time; - if (me.logOn.calculate) { - Ext.log('-- calculate ', this.getLayoutName(layout)); - } - frame = accum && accum.enter(); - time = Ext.perf.getTimestamp(); - ret = me.callParent(arguments); - time = Ext.perf.getTimestamp() - time; - if (accum) { - frame.leave(); - } - me.calcsByType[type] = (me.calcsByType[type] || 0) + 1; - me.timesByType[type] = (me.timesByType[type] || 0) + time; - /* add a / to the front of this line to enable layout completion logging - if (layout.done) { - var ownerContext = me.getCmp(layout.owner), - props = ownerContext.props; - - if (layout.isComponentLayout) { - Ext.log('complete ', layout.owner.id, ':', type, ' w=',props.width, ' h=', props.height); - } else { - Ext.log('complete ', layout.owner.id, ':', type, ' cw=',props.contentWidth, ' ch=', props.contentHeight); - } - }**/ - return ret; - } - } -}); -// End Diagnostics - -// @define Ext.layout.SizePolicy -/** - * This class describes how a layout will interact with a component it manages. - * - * There are special instances of this class stored as static properties to avoid object - * instantiation. All instances of this class should be treated as readonly objects. - * - * @class Ext.layout.SizePolicy - * @protected - */ -/** - * @property {Boolean} readsWidth - * Indicates that the `width` of the component is consumed. - * @readonly - */ -/** - * @property {Boolean} readsHeight - * Indicates that the `height` of the component is consumed. - * @readonly - */ -/** - * @property {Boolean} setsWidth - * Indicates that the `width` of the component will be set (i.e., calculated). - * @readonly - */ -/** - * @property {Boolean} setsHeight - * Indicates that the `height` of the component will be set (i.e., calculated). - * @readonly - */ - -/** - * Component layout for components which maintain an inner body element which must be resized to synchronize with the - * Component size. - * @private - */ -Ext.define('Ext.layout.component.Body', { - /* Begin Definitions */ - alias: [ - 'layout.body' - ], - extend: 'Ext.layout.component.Auto', - /* End Definitions */ - type: 'body', - beginLayout: function(ownerContext) { - this.callParent(arguments); - ownerContext.bodyContext = ownerContext.getEl('body'); - }, - beginLayoutCycle: function(ownerContext, firstCycle) { - var me = this, - lastWidthModel = me.lastWidthModel, - lastHeightModel = me.lastHeightModel, - body = me.owner.body; - me.callParent(arguments); - if (lastWidthModel && lastWidthModel.fixed && ownerContext.widthModel.shrinkWrap) { - body.setWidth(null); - } - if (lastHeightModel && lastHeightModel.fixed && ownerContext.heightModel.shrinkWrap) { - body.setHeight(null); - } - }, - // Padding is exciting here because we have 2 el's: owner.el and owner.body. Content - // size always includes the padding of the targetEl, which should be owner.body. But - // it is common to have padding on owner.el also (such as a panel header), so we need - // to do some more padding work if targetContext is not owner.el. The base class has - // already handled the ownerContext's frameInfo (border+framing) so all that is left - // is padding. - calculateOwnerHeightFromContentHeight: function(ownerContext, contentHeight) { - var height = this.callParent(arguments); - if (ownerContext.targetContext !== ownerContext) { - height += ownerContext.getPaddingInfo().height; - } - return height; - }, - calculateOwnerWidthFromContentWidth: function(ownerContext, contentWidth) { - var width = this.callParent(arguments); - if (ownerContext.targetContext !== ownerContext) { - width += ownerContext.getPaddingInfo().width; - } - return width; - }, - measureContentWidth: function(ownerContext) { - return ownerContext.bodyContext.setWidth(ownerContext.bodyContext.el.dom.offsetWidth, false); - }, - measureContentHeight: function(ownerContext) { - return ownerContext.bodyContext.setHeight(ownerContext.bodyContext.el.dom.offsetHeight, false); - }, - publishInnerHeight: function(ownerContext, height) { - var innerHeight = height - ownerContext.getFrameInfo().height, - targetContext = ownerContext.targetContext; - if (targetContext !== ownerContext) { - innerHeight -= ownerContext.getPaddingInfo().height; - } - // return the value here, it may get used in a subclass - return ownerContext.bodyContext.setHeight(innerHeight, !ownerContext.heightModel.natural); - }, - publishInnerWidth: function(ownerContext, width) { - var innerWidth = width - ownerContext.getFrameInfo().width, - targetContext = ownerContext.targetContext; - if (targetContext !== ownerContext) { - innerWidth -= ownerContext.getPaddingInfo().width; - } - ownerContext.bodyContext.setWidth(innerWidth, !ownerContext.widthModel.natural); - } -}); - -/** - * Component layout for Ext.form.FieldSet components - * @private - */ -Ext.define('Ext.layout.component.FieldSet', { - extend: 'Ext.layout.component.Body', - alias: [ - 'layout.fieldset' - ], - type: 'fieldset', - defaultCollapsedWidth: 100, - beforeLayoutCycle: function(ownerContext) { - if (ownerContext.target.collapsed) { - ownerContext.heightModel = this.sizeModels.shrinkWrap; - } - }, - beginLayout: function(ownerContext) { - var legend = this.owner.legend; - this.callParent([ - ownerContext - ]); - if (legend) { - ownerContext.legendContext = ownerContext.context.getCmp(legend); - } - }, - beginLayoutCycle: function(ownerContext) { - var target = ownerContext.target, - lastSize; - this.callParent(arguments); - // Each time we begin (2nd+ would be due to invalidate) we need to publish the - // known contentHeight if we are collapsed: - // - if (target.collapsed) { - ownerContext.setContentHeight(0); - // if we're collapsed, ignore a minHeight because it's likely going to - // be greater than the collapsed height - ownerContext.restoreMinHeight = target.minHeight; - delete target.minHeight; - // If we are also shrinkWrap width, we must provide a contentWidth (since the - // container layout is not going to run). - // - if (ownerContext.widthModel.shrinkWrap) { - lastSize = this.lastComponentSize; - ownerContext.setContentWidth((lastSize && lastSize.contentWidth) || this.defaultCollapsedWidth); - } - } - }, - finishedLayout: function(ownerContext) { - var owner = this.owner, - restore = ownerContext.restoreMinHeight; - this.callParent(arguments); - if (restore) { - owner.minHeight = restore; - } - }, - calculateOwnerWidthFromContentWidth: function(ownerContext, contentWidth) { - var legendContext = ownerContext.legendContext; - if (legendContext) { - contentWidth = Math.max(contentWidth, legendContext.getProp('width')); - } - return this.callParent([ - ownerContext, - contentWidth - ]); - }, - calculateOwnerHeightFromContentHeight: function(ownerContext, contentHeight) { - var border = ownerContext.getBorderInfo(), - legendContext = ownerContext.legendContext; - // Height of fieldset is content height plus top border width (which is either the - // legend height or top border width) plus bottom border width - return ownerContext.getProp('contentHeight') + ownerContext.getPaddingInfo().height + // In IE8m the top padding is on the body el - (Ext.isIE8 ? ownerContext.bodyContext.getPaddingInfo().top : 0) + (legendContext ? legendContext.getProp('height') : border.top) + border.bottom; - }, - publishInnerHeight: function(ownerContext, height) { - // Subtract the legend off here and pass it up to the body - // We do this because we don't want to set an incorrect body height - // and then setting it again with the correct value - var legendContext = ownerContext.legendContext, - legendHeight = 0; - if (legendContext) { - legendHeight = legendContext.getProp('height'); - } - if (legendHeight === undefined) { - this.done = false; - } else { - this.callParent([ - ownerContext, - height - legendHeight - ]); - } - }, - getLayoutItems: function() { - var legend = this.owner.legend; - return legend ? [ - legend - ] : []; - } -}); - -/** - * This is a layout that inherits the anchoring of {@link Ext.layout.container.Anchor} and adds the - * ability for x/y positioning using the standard x and y component config options. - * - * This class is intended to be extended or created via the {@link Ext.container.Container#layout layout} - * configuration property. See {@link Ext.container.Container#layout} for additional details. - * - * @example - * Ext.create('Ext.form.Panel', { - * title: 'Absolute Layout', - * width: 300, - * height: 275, - * layout: { - * type: 'absolute' - * // layout-specific configs go here - * //itemCls: 'x-abs-layout-item', - * }, - * url:'save-form.php', - * defaultType: 'textfield', - * items: [{ - * x: 10, - * y: 10, - * xtype:'label', - * text: 'Send To:' - * },{ - * x: 80, - * y: 10, - * name: 'to', - * anchor:'90%' // anchor width by percentage - * },{ - * x: 10, - * y: 40, - * xtype:'label', - * text: 'Subject:' - * },{ - * x: 80, - * y: 40, - * name: 'subject', - * anchor: '90%' // anchor width by percentage - * },{ - * x:0, - * y: 80, - * xtype: 'textareafield', - * name: 'msg', - * anchor: '100% 100%' // anchor width and height - * }], - * renderTo: Ext.getBody() - * }); - */ -Ext.define('Ext.layout.container.Absolute', { - /* Begin Definitions */ - alias: 'layout.absolute', - extend: 'Ext.layout.container.Anchor', - alternateClassName: 'Ext.layout.AbsoluteLayout', - /* End Definitions */ - targetCls: Ext.baseCSSPrefix + 'abs-layout-ct', - itemCls: Ext.baseCSSPrefix + 'abs-layout-item', - /** - * @cfg {Boolean} ignoreOnContentChange - * True indicates that changes to one item in this layout do not effect the layout in - * general. This may need to be set to false if the component is - * {@link Ext.Component#scrollable scrollable}. - */ - ignoreOnContentChange: true, - type: 'absolute', - /** - * @private - */ - adjustWidthAnchor: function(value, childContext) { - var padding = this.targetPadding, - x = childContext.getStyle('left'); - return value - x + padding.left; - }, - /** - * @private - */ - adjustHeightAnchor: function(value, childContext) { - var padding = this.targetPadding, - y = childContext.getStyle('top'); - return value - y + padding.top; - }, - isItemLayoutRoot: function(item) { - return this.ignoreOnContentChange || this.callParent(arguments); - }, - isItemShrinkWrap: function(item) { - return true; - }, - beginLayout: function(ownerContext) { - var me = this, - target = me.getTarget(); - me.callParent(arguments); - // Do not set position: relative; when the absolute layout target is the body - if (target.dom !== document.body) { - target.position(); - } - me.targetPadding = ownerContext.targetContext.getPaddingInfo(); - }, - isItemBoxParent: function(itemContext) { - return true; - }, - onContentChange: function() { - if (this.ignoreOnContentChange) { - return false; - } - return this.callParent(arguments); - }, - calculateContentSize: function(ownerContext, dimensions) { - var me = this, - containerDimensions = (dimensions || 0) | // jshint ignore:line - ((ownerContext.widthModel.shrinkWrap ? 1 : 0) | // jshint ignore:line - (ownerContext.heightModel.shrinkWrap ? 2 : 0)), - calcWidth = (containerDimensions & 1) || undefined, - // jshint ignore:line - calcHeight = (containerDimensions & 2) || undefined, - // jshint ignore:line - childItems = ownerContext.childItems, - length = childItems.length, - contentHeight = 0, - contentWidth = 0, - needed = 0, - props = ownerContext.props, - targetPadding, child, childContext, height, i, margins, width; - if (calcWidth) { - if (isNaN(props.contentWidth)) { - ++needed; - } else { - calcWidth = undefined; - } - } - if (calcHeight) { - if (isNaN(props.contentHeight)) { - ++needed; - } else { - calcHeight = undefined; - } - } - if (needed) { - for (i = 0; i < length; ++i) { - childContext = childItems[i]; - child = childContext.target; - height = calcHeight && childContext.getProp('height'); - width = calcWidth && childContext.getProp('width'); - margins = childContext.getMarginInfo(); - height += margins.bottom; - width += margins.right; - contentHeight = Math.max(contentHeight, (child.y || 0) + height); - contentWidth = Math.max(contentWidth, (child.x || 0) + width); - if (isNaN(contentHeight) && isNaN(contentWidth)) { - me.done = false; - return; - } - } - if (calcWidth || calcHeight) { - targetPadding = ownerContext.targetContext.getPaddingInfo(); - } - if (calcWidth && !ownerContext.setContentWidth(contentWidth + targetPadding.width)) { - me.done = false; - } - if (calcHeight && !ownerContext.setContentHeight(contentHeight + targetPadding.height)) { - me.done = false; - } - } - } -}); -/* add a '/' to turn on this log ('//* enables, '/*' disables) - if (me.done) { - var el = ownerContext.targetContext.el.dom; - Ext.log(this.owner.id, '.contentSize: ', contentWidth, 'x', contentHeight, - ' => scrollSize: ', el.scrollWidth, 'x', el.scrollHeight); - }/**/ - -/** - * This is a layout that manages multiple Panels in an expandable accordion style such that by default only - * one Panel can be expanded at any given time (set {@link #multi} config to have more open). Each Panel has - * built-in support for expanding and collapsing. - * - * Note: Only Ext Panels and all subclasses of Ext.panel.Panel may be used in an accordion layout Container. - * - * @example - * Ext.create('Ext.panel.Panel', { - * title: 'Accordion Layout', - * width: 300, - * height: 300, - * defaults: { - * // applied to each contained panel - * bodyStyle: 'padding:15px' - * }, - * layout: { - * // layout-specific configs go here - * type: 'accordion', - * titleCollapse: false, - * animate: true, - * activeOnTop: true - * }, - * items: [{ - * title: 'Panel 1', - * html: 'Panel content!' - * },{ - * title: 'Panel 2', - * html: 'Panel content!' - * },{ - * title: 'Panel 3', - * html: 'Panel content!' - * }], - * renderTo: Ext.getBody() - * }); - */ -Ext.define('Ext.layout.container.Accordion', { - extend: 'Ext.layout.container.VBox', - alias: 'layout.accordion', - type: 'accordion', - alternateClassName: 'Ext.layout.AccordionLayout', - targetCls: Ext.baseCSSPrefix + 'accordion-layout-ct', - itemCls: [ - Ext.baseCSSPrefix + 'box-item', - Ext.baseCSSPrefix + 'accordion-item' - ], - align: 'stretch', - enableSplitters: false, - /** - * @cfg {Boolean} fill - * True to adjust the active item's height to fill the available space in the container, false to use the - * item's current height, or auto height if not explicitly set. - */ - fill: true, - /** - * @cfg {Boolean} autoWidth - * Child Panels have their width actively managed to fit within the accordion's width. - * @removed This config is ignored in ExtJS 4 - */ - /** - * @cfg {Boolean} titleCollapse - * True to allow expand/collapse of each contained panel by clicking anywhere on the title bar, false to allow - * expand/collapse only when the toggle tool button is clicked. When set to false, - * {@link #hideCollapseTool} should be false also. An explicit {@link Ext.panel.Panel#titleCollapse} declared - * on the panel will override this setting. - */ - titleCollapse: true, - /** - * @cfg {Boolean} hideCollapseTool - * True to hide the contained Panels' collapse/expand toggle buttons, false to display them. - * When set to true, {@link #titleCollapse} is automatically set to true. - */ - hideCollapseTool: false, - /** - * @cfg {Boolean} collapseFirst - * True to make sure the collapse/expand toggle button always renders first (to the left of) any other tools - * in the contained Panels' title bars, false to render it last. By default, this will use the - * {@link Ext.panel.Panel#collapseFirst} setting on the panel. If the config option is specified on the layout, - * it will override the panel value. - */ - collapseFirst: undefined, - /** - * @cfg {Boolean} animate - * True to slide the contained panels open and closed during expand/collapse using animation, false to open and - * close directly with no animation. Note: The layout performs animated collapsing - * and expanding, *not* the child Panels. - */ - animate: true, - /** - * @cfg {Boolean} activeOnTop - * Only valid when {@link #multi} is `false` and {@link #animate} is `false`. - * - * True to swap the position of each panel as it is expanded so that it becomes the first item in the container, - * false to keep the panels in the rendered order. - */ - activeOnTop: false, - /** - * @cfg {Boolean} multi - * Set to true to enable multiple accordion items to be open at once. - */ - multi: false, - /** - * @cfg {Boolean} [wrapOver=true] When `true`, pressing Down or Right arrow key on the - * focused last accordion panel header will navigate to the first panel; pressing Up - * or Left arrow key on the focused first accordion panel header will navigate to the - * last panel. - * Set this to `false` to prevent keyboard navigation from wrapping over the edges. - */ - wrapOver: true, - panelCollapseMode: 'header', - defaultAnimatePolicy: { - y: true, - height: true - }, - constructor: function() { - var me = this; - me.callParent(arguments); - if (me.animate) { - me.animatePolicy = {}; - /* Animate our parallel dimension and position. - So in the default vertical accordion, this will be - { - y: true, - height: true - } - */ - me.animatePolicy[me.names.x] = true; - me.animatePolicy[me.names.width] = true; - } else { - me.animatePolicy = null; - } - }, - beforeRenderItems: function(items) { - var me = this, - ln = items.length, - owner = me.owner, - collapseFirst = me.collapseFirst, - hasCollapseFirst = Ext.isDefined(collapseFirst), - expandedItem = me.getExpanded(true)[0], - multi = me.multi, - comp, i; - for (i = 0; i < ln; i++) { - comp = items[i]; - if (!comp.rendered) { - // Set up initial properties for Panels in an accordion. - comp.isAccordionPanel = true; - comp.bodyAriaRole = 'tabpanel'; - comp.accordionWrapOver = me.wrapOver; - if (!multi || comp.collapsible !== false) { - comp.collapsible = true; - } - if (comp.collapsible) { - if (hasCollapseFirst) { - comp.collapseFirst = collapseFirst; - } - if (me.hideCollapseTool) { - comp.hideCollapseTool = me.hideCollapseTool; - comp.titleCollapse = true; - } else if (me.titleCollapse && comp.titleCollapse === undefined) { - // Only force titleCollapse if we don't explicitly - // set one on the child panel - comp.titleCollapse = me.titleCollapse; - } - } - comp.hideHeader = comp.width = null; - comp.title = comp.title || ' '; - comp.addBodyCls(Ext.baseCSSPrefix + 'accordion-body'); - // If only one child Panel is allowed to be expanded - // then collapse all except the first one found with collapsed:false - // If we have hasExpanded set, we've already done this - if (!multi) { - if (expandedItem) { - comp.collapsed = expandedItem !== comp; - } else if (comp.hasOwnProperty('collapsed') && comp.collapsed === false) { - expandedItem = comp; - } else { - comp.collapsed = true; - } - // If only one child Panel may be expanded, then intercept expand/show requests. - owner.mon(comp, 'show', me.onComponentShow, me); - } - // Need to still check this outside multi because we don't want - // a single item to be able to collapse - comp.headerOverCls = Ext.baseCSSPrefix + 'accordion-hd-over'; - } - } - // If no collapsed:false Panels found, make the first one expanded, only if we're - // not during an expand/collapse - if (!me.processing && !multi) { - if (!expandedItem) { - if (ln) { - items[0].collapsed = false; - } - } else if (me.activeOnTop) { - expandedItem.collapsed = false; - me.configureItem(expandedItem); - if (owner.items.indexOf(expandedItem) > 0) { - owner.insert(0, expandedItem); - } - } - } - }, - getItemsRenderTree: function(items) { - this.beforeRenderItems(items); - return this.callParent(arguments); - }, - renderItems: function(items, target) { - this.beforeRenderItems(items); - this.callParent(arguments); - }, - configureItem: function(item) { - this.callParent(arguments); - // Accordion headers are immune to dock layout's border-management rules - item.ignoreHeaderBorderManagement = true; - // We handle animations for the expand/collapse of items. - // Items do not have individual borders - item.animCollapse = false; - // If filling available space, all Panels flex. - if (this.fill) { - item.flex = 1; - } - }, - beginLayout: function(ownerContext) { - this.callParent(arguments); - // Accordion widgets have the role of tablist along with the attribute - // aria-multiselectable="true" to indicate that it's an accordion - // and not just a simple tab panel. - // We can't set this role on the panel's main el as this panel may be - // a region in a border layout which yields its own set of ARIA attributes. - // We also can't set this role on panel's body el, because the panel could be - // a FormPanel that would have role="form" on the body el, and the tablist - // needs to be contained within it. - // innerCt seems to be the most logical choice here. - this.innerCt.dom.setAttribute('role', 'tablist'); - this.innerCt.dom.setAttribute('aria-multiselectable', true); - this.updatePanelClasses(ownerContext); - }, - updatePanelClasses: function(ownerContext) { - var children = ownerContext.visibleItems, - ln = children.length, - siblingCollapsed = true, - i, child, header; - for (i = 0; i < ln; i++) { - child = children[i]; - header = child.header; - header.addCls(Ext.baseCSSPrefix + 'accordion-hd'); - if (siblingCollapsed) { - header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded'); - } else { - header.addCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded'); - } - if (i + 1 === ln && child.collapsed) { - header.addCls(Ext.baseCSSPrefix + 'accordion-hd-last-collapsed'); - } else { - header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-last-collapsed'); - } - siblingCollapsed = child.collapsed; - } - }, - // When a Component expands, adjust the heights of the other Components to be just enough to accommodate - // their headers. - // The expanded Component receives the only flex value, and so gets all remaining space. - onBeforeComponentExpand: function(toExpand) { - var me = this, - owner = me.owner, - multi = me.multi, - moveToTop = !multi && !me.animate && me.activeOnTop, - expanded, previousValue, anim; - if (!me.processing) { - me.processing = true; - previousValue = owner.deferLayouts; - owner.deferLayouts = true; - if (!multi) { - expanded = me.getExpanded()[0]; - if (expanded && expanded !== toExpand) { - anim = expanded.$layoutAnim; - // If the item is animating, finish it. - if (anim) { - anim.jumpToEnd(); - } - expanded.collapse(); - } - } - if (moveToTop) { - // Prevent extra layout when moving the item - Ext.suspendLayouts(); - owner.insert(0, toExpand); - Ext.resumeLayouts(); - } - owner.deferLayouts = previousValue; - me.processing = false; - } - }, - onBeforeComponentCollapse: function(comp) { - var me = this, - owner = me.owner, - toExpand, expanded, previousValue; - if (me.owner.items.getCount() === 1) { - // do not allow collapse if there is only one item - return false; - } - if (!me.processing) { - me.processing = true; - previousValue = owner.deferLayouts; - owner.deferLayouts = true; - toExpand = comp.next() || comp.prev(); - // If we are allowing multi, and the "toCollapse" component is NOT the only expanded Component, - // then ask the box layout to collapse it to its header. - if (me.multi) { - expanded = me.getExpanded(); - // If the collapsing Panel is the only expanded one, expand the following Component. - // All this is handling fill: true, so there must be at least one expanded, - if (expanded.length === 1) { - toExpand.expand(); - } - } else if (toExpand) { - toExpand.expand(); - } - owner.deferLayouts = previousValue; - me.processing = false; - } - }, - onComponentShow: function(comp) { - this.onBeforeComponentExpand(comp); - }, - onAdd: function(item) { - var me = this; - me.callParent(arguments); - if (item.collapseMode === 'placeholder') { - item.collapseMode = me.panelCollapseMode; - } - item.collapseDirection = item.headerPosition; - // If we add to an accordion after its is has run once we need to make sure - // new items are collapsed on entry. The item is also in the collection now, - // so only collapse it if we have more than 1. - if (me.layoutCount && !me.multi && me.owner.items.getCount() > 1) { - // If we get here, we must already have something expanded, so we don't - // want to react here. - me.processing = true; - item.collapse(); - me.processing = false; - } - }, - onRemove: function(panel, destroying) { - var me = this, - item; - me.callParent(arguments); - if (!me.owner.destroying && !me.multi && !panel.collapsed) { - item = me.owner.items.first(); - if (item) { - item.expand(); - } - } - }, - getExpanded: function(explicitCheck) { - var items = this.owner.items.items, - len = items.length, - i = 0, - out = [], - add, item; - for (; i < len; ++i) { - item = items[i]; - if (!item.hidden) { - if (explicitCheck) { - add = item.hasOwnProperty('collapsed') && item.collapsed === false; - } else { - add = !item.collapsed; - } - if (add) { - out.push(item); - } - } - } - return out; - }, - // No need to run an extra layout since everything has already achieved the - // desired size when using an accordion. - afterCollapse: Ext.emptyFn, - afterExpand: Ext.emptyFn -}); - -/** - * Private utility class for Ext.layout.container.Border. - * @private - */ -Ext.define('Ext.resizer.BorderSplitter', { - extend: 'Ext.resizer.Splitter', - uses: [ - 'Ext.resizer.BorderSplitterTracker' - ], - alias: 'widget.bordersplitter', - // must be configured in by the border layout: - collapseTarget: null, - getTrackerConfig: function() { - var trackerConfig = this.callParent(); - trackerConfig.xclass = 'Ext.resizer.BorderSplitterTracker'; - return trackerConfig; - }, - onTargetCollapse: function(target) { - this.callParent([ - target - ]); - if (this.performCollapse !== false && target.collapseMode == 'mini') { - target.addCls(target.baseCls + '-' + target.collapsedCls + '-mini'); - } - }, - onTargetExpand: function(target) { - this.callParent([ - target - ]); - if (this.performCollapse !== false && target.collapseMode == 'mini') { - target.removeCls(target.baseCls + '-' + target.collapsedCls + '-mini'); - } - } -}); - -/** - * This is a multi-pane, application-oriented UI layout style that supports multiple nested panels, automatic bars - * between regions and built-in {@link Ext.panel.Panel#collapsible expanding and collapsing} of regions. - * - * This class is intended to be extended or created via the `layout:'border'` {@link Ext.container.Container#layout} - * config, and should generally not need to be created directly via the new keyword. - * - * @example - * Ext.create('Ext.panel.Panel', { - * width: 500, - * height: 300, - * title: 'Border Layout', - * layout: 'border', - * items: [{ - * title: 'South Region is resizable', - * region: 'south', // position for region - * xtype: 'panel', - * height: 100, - * split: true, // enable resizing - * margin: '0 5 5 5' - * },{ - * // xtype: 'panel' implied by default - * title: 'West Region is collapsible', - * region:'west', - * xtype: 'panel', - * margin: '5 0 0 5', - * width: 200, - * collapsible: true, // make collapsible - * id: 'west-region-container', - * layout: 'fit' - * },{ - * title: 'Center Region', - * region: 'center', // center region is required, no width/height specified - * xtype: 'panel', - * layout: 'fit', - * margin: '5 5 0 0' - * }], - * renderTo: Ext.getBody() - * }); - * - * # Notes - * - * - When using the split option, the layout will automatically insert a {@link Ext.resizer.Splitter} - * into the appropriate place. This will modify the underlying - * {@link Ext.container.Container#property-items items} collection in the container. - * - * - Any Container using the Border layout **must** have a child item with `region:'center'`. - * The child item in the center region will always be resized to fill the remaining space - * not used by the other regions in the layout. - * - * - Any child items with a region of `west` or `east` may be configured with either an initial - * `width`, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage width - * **string** (Which is simply divided by 100 and used as a flex value). - * The 'center' region has a flex value of `1`. - * - * - Any child items with a region of `north` or `south` may be configured with either an initial - * `height`, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage height - * **string** (Which is simply divided by 100 and used as a flex value). - * The 'center' region has a flex value of `1`. - * - * - **There is no BorderLayout.Region class in ExtJS 4.0+** - */ -Ext.define('Ext.layout.container.Border', { - extend: 'Ext.layout.container.Container', - alias: 'layout.border', - alternateClassName: 'Ext.layout.BorderLayout', - requires: [ - 'Ext.resizer.BorderSplitter', - 'Ext.fx.Anim', - // Overrides for Panel that provide border layout features - 'Ext.layout.container.border.Region' - ], - targetCls: Ext.baseCSSPrefix + 'border-layout-ct', - itemCls: [ - Ext.baseCSSPrefix + 'border-item', - Ext.baseCSSPrefix + 'box-item' - ], - type: 'border', - isBorderLayout: true, - /** - * @cfg {Boolean/Ext.resizer.BorderSplitter} split - * This configuration option is to be applied to the **child `items`** managed by this layout. - * Each region with `split:true` will get a {@link Ext.resizer.BorderSplitter Splitter} that - * allows for manual resizing of the container. Except for the `center` region. - * - * This option can also accept an object of configurations from the {@link Ext.resizer.BorderSplitter}. - * An example of this would be: - * - * { - * title: 'North', - * region: 'north', - * height: 100, - * collapsible: true, - * split: { - * size: 20 - * } - * } - */ - /** - * @cfg {Boolean} [splitterResize=true] - * This configuration option is to be applied to the **child `items`** managed by this layout and - * is used in conjunction with {@link #split}. By default, when specifying {@link #split}, the region - * can be dragged to be resized. Set this option to false to show the split bar but prevent resizing. - */ - /** - * @cfg {Number/String/Object} padding - * Sets the padding to be applied to all child items managed by this layout. - * - * This property can be specified as a string containing space-separated, numeric - * padding values. The order of the sides associated with each value matches the way - * CSS processes padding values: - * - * - If there is only one value, it applies to all sides. - * - If there are two values, the top and bottom borders are set to the first value - * and the right and left are set to the second. - * - If there are three values, the top is set to the first value, the left and right - * are set to the second, and the bottom is set to the third. - * - If there are four values, they apply to the top, right, bottom, and left, - * respectively. - * - */ - padding: undefined, - percentageRe: /(\d+)%/, - horzPositionProp: 'left', - padOnContainerProp: 'left', - padNotOnContainerProp: 'right', - /** - * Reused meta-data objects that describe axis properties. - * @private - */ - axisProps: { - horz: { - borderBegin: 'west', - borderEnd: 'east', - horizontal: true, - posProp: 'x', - sizeProp: 'width', - sizePropCap: 'Width' - }, - vert: { - borderBegin: 'north', - borderEnd: 'south', - horizontal: false, - posProp: 'y', - sizeProp: 'height', - sizePropCap: 'Height' - } - }, - /** - * @private - */ - centerRegion: null, - manageMargins: true, - panelCollapseAnimate: true, - panelCollapseMode: 'placeholder', - /** - * @cfg {Object} regionWeights - * The default weights to assign to regions in the border layout. These values are - * used when a region does not contain a `weight` property. This object must have - * properties for all regions ("north", "south", "east" and "west"). - * - * **IMPORTANT:** Since this is an object, changing its properties will impact ALL - * instances of Border layout. If this is not desired, provide a replacement object as - * a config option instead: - * - * layout: { - * type: 'border', - * regionWeights: { - * west: 20, - * north: 10, - * south: -10, - * east: -20 - * } - * } - * - * The region with the highest weight is assigned space from the border before other - * regions. Regions of equal weight are assigned space based on their position in the - * owner's items list (first come, first served). - */ - regionWeights: { - north: 20, - south: 10, - center: 0, - west: -10, - east: -20 - }, - //---------------------------------- - // Layout processing - /** - * Creates the axis objects for the layout. These are only missing size information - * which is added during {@link #calculate}. - * @private - */ - beginAxis: function(ownerContext, regions, name) { - var me = this, - props = me.axisProps[name], - isVert = !props.horizontal, - sizeProp = props.sizeProp, - totalFlex = 0, - childItems = ownerContext.childItems, - length = childItems.length, - center, i, childContext, centerFlex, comp, region, match, size, type, target, placeholder; - for (i = 0; i < length; ++i) { - childContext = childItems[i]; - comp = childContext.target; - childContext.layoutPos = {}; - if (comp.region) { - childContext.region = region = comp.region; - childContext.isCenter = comp.isCenter; - childContext.isHorz = comp.isHorz; - childContext.isVert = comp.isVert; - childContext.weight = comp.weight || me.regionWeights[region] || 0; - comp.weight = childContext.weight; - regions[comp.id] = childContext; - if (comp.isCenter) { - center = childContext; - centerFlex = comp.flex; - ownerContext.centerRegion = center; - - continue; - } - if (isVert !== childContext.isVert) { - - continue; - } - // process regions "isVert ? north||south : east||center||west" - childContext.reverseWeighting = (region === props.borderEnd); - size = comp[sizeProp]; - type = typeof size; - if (!comp.collapsed) { - if (type === 'string' && (match = me.percentageRe.exec(size))) { - childContext.percentage = parseInt(match[1], 10); - } else if (comp.flex) { - totalFlex += childContext.flex = comp.flex; - } - } - } - } - // Special cases for a collapsed center region - if (center) { - target = center.target; - if ((placeholder = target.placeholderFor)) { - if (!centerFlex && isVert === placeholder.collapsedVertical()) { - // The center region is a placeholder, collapsed in this axis - centerFlex = 0; - center.collapseAxis = name; - } - } else if (target.collapsed && (isVert === target.collapsedVertical())) { - // The center region is a collapsed header, collapsed in this axis - centerFlex = 0; - center.collapseAxis = name; - } - } - if (centerFlex == null) { - // If we still don't have a center flex, default to 1 - centerFlex = 1; - } - totalFlex += centerFlex; - return Ext.apply({ - before: isVert ? 'top' : 'left', - totalFlex: totalFlex - }, props); - }, - beginLayout: function(ownerContext) { - var me = this, - items = me.getLayoutItems(), - pad = me.padding, - type = typeof pad, - padOnContainer = false, - childContext, item, length, i, regions, collapseTarget, doShow, hidden, region; - // TODO: EXTJSIV-13015 - if (ownerContext.heightModel.shrinkWrap) { - Ext.raise("Border layout does not currently support shrinkWrap height. " + "Please specify a height on component: " + me.owner.id + ", or use a container layout that sets the component's height."); - } - if (ownerContext.widthModel.shrinkWrap) { - Ext.raise("Border layout does not currently support shrinkWrap width. " + "Please specify a width on component: " + me.owner.id + ", or use a container layout that sets the component's width."); - } - // We sync the visibility state of splitters with their region: - if (pad) { - if (type === 'string' || type === 'number') { - pad = Ext.util.Format.parseBox(pad); - } - } else { - pad = ownerContext.getEl('getTargetEl').getPaddingInfo(); - padOnContainer = true; - } - ownerContext.outerPad = pad; - ownerContext.padOnContainer = padOnContainer; - for (i = 0 , length = items.length; i < length; ++i) { - item = items[i]; - collapseTarget = me.getSplitterTarget(item); - if (collapseTarget) { - // if (splitter) - doShow = undefined; - hidden = !!item.hidden; - if (!collapseTarget.split) { - if (collapseTarget.isCollapsingOrExpanding) { - doShow = !!collapseTarget.collapsed; - } - } else if (hidden !== collapseTarget.hidden) { - doShow = !collapseTarget.hidden; - } - if (doShow) { - item.show(); - } else if (doShow === false) { - item.hide(); - } - } - } - // The above synchronized visibility of splitters with their regions, so we need - // to make this call after that so that childItems and visibleItems are correct: - // - me.callParent(arguments); - items = ownerContext.childItems; - length = items.length; - regions = {}; - ownerContext.borderAxisHorz = me.beginAxis(ownerContext, regions, 'horz'); - ownerContext.borderAxisVert = me.beginAxis(ownerContext, regions, 'vert'); - // Now that weights are assigned to the region's contextItems, we assign those - // same weights to the contextItem for the splitters. We also cross link the - // contextItems for the collapseTarget and its splitter. - for (i = 0; i < length; ++i) { - childContext = items[i]; - collapseTarget = me.getSplitterTarget(childContext.target); - if (collapseTarget) { - // if (splitter) - region = regions[collapseTarget.id]; - if (!region) { - // if the region was hidden it will not be part of childItems, and - // so beginAxis() won't add it to the regions object, so we have - // to create the context item here. - region = ownerContext.getEl(collapseTarget.el, me); - region.region = collapseTarget.region; - } - childContext.collapseTarget = collapseTarget = region; - childContext.weight = collapseTarget.weight; - childContext.reverseWeighting = collapseTarget.reverseWeighting; - collapseTarget.splitter = childContext; - childContext.isHorz = collapseTarget.isHorz; - childContext.isVert = collapseTarget.isVert; - } - } - // Now we want to sort the childItems by their weight. - me.sortWeightedItems(items, 'reverseWeighting'); - me.setupSplitterNeighbors(items); - }, - calculate: function(ownerContext) { - var me = this, - containerSize = me.getContainerSize(ownerContext), - childItems = ownerContext.childItems, - length = childItems.length, - horz = ownerContext.borderAxisHorz, - vert = ownerContext.borderAxisVert, - pad = ownerContext.outerPad, - padOnContainer = ownerContext.padOnContainer, - i, childContext, childMargins, size, horzPercentTotal, vertPercentTotal; - horz.begin = pad[me.padOnContainerProp]; - vert.begin = pad.top; - // If the padding is already on the container we need to add it to the space - // If not on the container, it's "virtual" padding. - horzPercentTotal = horz.end = horz.flexSpace = containerSize.width + (padOnContainer ? pad[me.padOnContainerProp] : -pad[me.padNotOnContainerProp]); - vertPercentTotal = vert.end = vert.flexSpace = containerSize.height + (padOnContainer ? pad.top : -pad.bottom); - // Reduce flexSpace on each axis by the fixed/auto sized dimensions of items that - // aren't flexed along that axis. - for (i = 0; i < length; ++i) { - childContext = childItems[i]; - childMargins = childContext.getMarginInfo(); - // Margins are always fixed size and must be removed from the space used for percentages and flexes - if (childContext.isHorz || childContext.isCenter) { - horz.addUnflexed(childMargins.width); - horzPercentTotal -= childMargins.width; - } - if (childContext.isVert || childContext.isCenter) { - vert.addUnflexed(childMargins.height); - vertPercentTotal -= childMargins.height; - } - // Fixed size components must have their sizes removed from the space used for flex - if (!childContext.flex && !childContext.percentage) { - if (childContext.isHorz || (childContext.isCenter && childContext.collapseAxis === 'horz')) { - size = childContext.getProp('width'); - horz.addUnflexed(size); - // splitters should not count towards percentages - if (childContext.collapseTarget) { - horzPercentTotal -= size; - } - } else if (childContext.isVert || (childContext.isCenter && childContext.collapseAxis === 'vert')) { - size = childContext.getProp('height'); - vert.addUnflexed(size); - // splitters should not count towards percentages - if (childContext.collapseTarget) { - vertPercentTotal -= size; - } - } - } - } - // else ignore center since it is fully flexed - for (i = 0; i < length; ++i) { - childContext = childItems[i]; - childMargins = childContext.getMarginInfo(); - // Calculate the percentage sizes. After this calculation percentages are very similar to fixed sizes - if (childContext.percentage) { - if (childContext.isHorz) { - size = Math.ceil(horzPercentTotal * childContext.percentage / 100); - size = childContext.setWidth(size); - horz.addUnflexed(size); - } else if (childContext.isVert) { - size = Math.ceil(vertPercentTotal * childContext.percentage / 100); - size = childContext.setHeight(size); - vert.addUnflexed(size); - } - } - } - // center shouldn't have a percentage but if it does it should be ignored - // If we haven't gotten sizes for all unflexed dimensions on an axis, the flexSpace - // will be NaN so we won't be calculating flexed dimensions until that is resolved. - for (i = 0; i < length; ++i) { - childContext = childItems[i]; - if (!childContext.isCenter) { - me.calculateChildAxis(childContext, horz); - me.calculateChildAxis(childContext, vert); - } - } - // Once all items are placed, the final size of the center can be determined. If we - // can determine both width and height, we are done. We use '+' instead of '&&' to - // avoid short-circuiting (we want to call both): - if (me.finishAxis(ownerContext, vert) + me.finishAxis(ownerContext, horz) < 2) { - me.done = false; - } else { - // Size information is published as we place regions but position is hard to do - // that way (while avoiding published multiple times) so we publish all the - // positions at the end. - me.finishPositions(childItems); - } - }, - /** - * Performs the calculations for a region on a specified axis. - * @private - */ - calculateChildAxis: function(childContext, axis) { - var collapseTarget = childContext.collapseTarget, - setSizeMethod = 'set' + axis.sizePropCap, - sizeProp = axis.sizeProp, - childMarginSize = childContext.getMarginInfo()[sizeProp], - region, isBegin, flex, pos, size; - if (collapseTarget) { - // if (splitter) - region = collapseTarget.region; - } else { - region = childContext.region; - flex = childContext.flex; - } - isBegin = region === axis.borderBegin; - if (!isBegin && region !== axis.borderEnd) { - // a north/south region on the horizontal axis or an east/west region on the - // vertical axis: stretch to fill remaining space: - childContext[setSizeMethod](axis.end - axis.begin - childMarginSize); - pos = axis.begin; - } else { - if (flex) { - size = Math.ceil(axis.flexSpace * (flex / axis.totalFlex)); - size = childContext[setSizeMethod](size); - } else if (childContext.percentage) { - // Like getProp but without registering a dependency - we calculated the size, we don't depend on it - size = childContext.peek(sizeProp); - } else { - size = childContext.getProp(sizeProp); - } - size += childMarginSize; - if (isBegin) { - pos = axis.begin; - axis.begin += size; - } else { - axis.end = pos = axis.end - size; - } - } - childContext.layoutPos[axis.posProp] = pos; - }, - eachItem: function(region, fn, scope) { - var me = this, - items = me.getLayoutItems(), - i = 0, - item; - if (Ext.isFunction(region)) { - fn = region; - scope = fn; - } - for (i; i < items.length; i++) { - item = items[i]; - if (!region || item.region === region) { - if (fn.call(scope, item) === false) { - break; - } - } - } - }, - /** - * Finishes the calculations on an axis. This basically just assigns the remaining - * space to the center region. - * @private - */ - finishAxis: function(ownerContext, axis) { - var size = axis.end - axis.begin, - center = ownerContext.centerRegion; - if (center) { - center['set' + axis.sizePropCap](size - center.getMarginInfo()[axis.sizeProp]); - center.layoutPos[axis.posProp] = axis.begin; - } - return Ext.isNumber(size) ? 1 : 0; - }, - /** - * Finishes by setting the positions on the child items. - * @private - */ - finishPositions: function(childItems) { - var length = childItems.length, - index, childContext, - marginProp = this.horzPositionProp; - for (index = 0; index < length; ++index) { - childContext = childItems[index]; - childContext.setProp('x', childContext.layoutPos.x + childContext.marginInfo[marginProp]); - childContext.setProp('y', childContext.layoutPos.y + childContext.marginInfo.top); - } - }, - getLayoutItems: function() { - var owner = this.owner, - ownerItems = (owner && owner.items && owner.items.items) || [], - length = ownerItems.length, - items = [], - i = 0, - ownerItem, placeholderFor; - for (; i < length; i++) { - ownerItem = ownerItems[i]; - placeholderFor = ownerItem.placeholderFor; - // There are a couple of scenarios where we do NOT want an item to - // be included in the layout items: - // - // 1. If the item is floated. This can happen when a region's header - // is clicked to "float" the item, then another region's header or - // is clicked quickly before the first floated region has had a - // chance to slide out. When this happens, the second click triggers - // a layout, the purpose of which is to determine what the size of the - // second region will be after it is floated, so it can be animated - // to that size. In this case the existing floated item should not be - // included in the layout items because it will not be visible once - // it's slideout animation has completed. - // - // 2. If the item is a placeholder for a panel that is currently - // being expanded. Similar to scenario 1, a second layout can be - // triggered by another panel being expanded/collapsed/floated before - // the first panel has finished it's expand animation. If this is the - // case we do not want the placeholder to be included in the layout - // items because it will not be once the panel has finished expanding. - // - // If the component is hidden, we need none of these shenanigans - if (ownerItem.hidden || ((!ownerItem.floated || ownerItem.isCollapsingOrExpanding === 2) && !(placeholderFor && placeholderFor.isCollapsingOrExpanding === 2))) { - items.push(ownerItem); - } - } - return items; - }, - getPlaceholder: function(comp) { - return comp.getPlaceholder && comp.getPlaceholder(); - }, - getMaxWeight: function(region) { - return this.getMinMaxWeight(region); - }, - getMinWeight: function(region) { - return this.getMinMaxWeight(region, true); - }, - getMinMaxWeight: function(region, min) { - var me = this, - weight = null; - me.eachItem(region, function(item) { - if (item.hasOwnProperty('weight')) { - if (weight === null) { - weight = item.weight; - return; - } - if ((min && item.weight < weight) || item.weight > weight) { - weight = item.weight; - } - } - }, this); - return weight; - }, - getSplitterTarget: function(splitter) { - var collapseTarget = splitter.collapseTarget; - if (collapseTarget && collapseTarget.collapsed) { - return collapseTarget.placeholder || collapseTarget; - } - return collapseTarget; - }, - isItemBoxParent: function(itemContext) { - return true; - }, - isItemShrinkWrap: function(item) { - return true; - }, - //---------------------------------- - // Event handlers - /** - * Inserts the splitter for a given region. A reference to the splitter is also stored - * on the component as "splitter". - * @private - */ - insertSplitter: function(item, index, hidden, splitterCfg) { - var region = item.region, - splitter = Ext.apply({ - xtype: 'bordersplitter', - collapseTarget: item, - id: item.id + '-splitter', - hidden: hidden, - canResize: item.splitterResize !== false, - splitterFor: item, - synthetic: true - }, // not user-defined - splitterCfg), - at = index + ((region === 'south' || region === 'east') ? 0 : 1); - if (item.collapseMode === 'mini') { - splitter.collapsedCls = item.collapsedCls; - } - item.splitter = this.owner.add(at, splitter); - }, - getMoveAfterIndex: function(after) { - var index = this.callParent(arguments); - if (after.splitter) { - index++; - } - return index; - }, - moveItemBefore: function(item, before) { - var beforeRegion; - if (before && before.splitter) { - beforeRegion = before.region; - if (beforeRegion === 'south' || beforeRegion === 'east') { - before = before.splitter; - } - } - return this.callParent([ - item, - before - ]); - }, - /** - * Called when a region (actually when any component) is added to the container. The - * region is decorated with some helpful properties (isCenter, isHorz, isVert) and its - * splitter is added if its "split" property is true. - * @private - */ - onAdd: function(item, index) { - var me = this, - placeholderFor = item.placeholderFor, - region = item.region, - isCenter, split, hidden, cfg; - me.callParent(arguments); - if (region) { - Ext.apply(item, me.regionFlags[region]); - if (me.owner.isViewport) { - item.isViewportBorderChild = true; - } - if (item.initBorderRegion) { - // This method should always be present but perhaps the override is being - // excluded. - item.initBorderRegion(); - } - isCenter = region === 'center'; - if (isCenter) { - if (me.centerRegion) { - Ext.raise("Cannot have multiple center regions in a BorderLayout."); - } - me.centerRegion = item; - } else { - split = item.split; - hidden = !!item.hidden; - if (typeof split === 'object') { - cfg = split; - split = true; - } - if ((item.isHorz || item.isVert) && (split || item.collapseMode === 'mini')) { - me.insertSplitter(item, index, hidden || !split, cfg); - } - } - if (!isCenter && !item.hasOwnProperty('collapseMode')) { - item.collapseMode = me.panelCollapseMode; - } - if (!item.hasOwnProperty('animCollapse')) { - if (item.collapseMode !== 'placeholder') { - // other collapse modes do not animate nicely in a border layout, so - // default them to off: - item.animCollapse = false; - } else { - item.animCollapse = me.panelCollapseAnimate; - } - } - } else if (placeholderFor) { - Ext.apply(item, me.regionFlags[placeholderFor.region]); - item.region = placeholderFor.region; - item.weight = placeholderFor.weight; - } - }, - onDestroy: function() { - this.centerRegion = null; - this.callParent(); - }, - onRemove: function(comp, isDestroying) { - var me = this, - region = comp.region, - splitter = comp.splitter, - owner = me.owner, - destroying = owner.destroying, - el; - if (region) { - if (comp.isCenter) { - me.centerRegion = null; - } - delete comp.isCenter; - delete comp.isHorz; - delete comp.isVert; - // If the owner is destroying, the splitter will be cleared anyway - if (splitter && !owner.destroying) { - owner.doRemove(splitter, true); - } - // avoid another layout - delete comp.splitter; - } - me.callParent(arguments); - if (!destroying && !isDestroying && comp.rendered) { - // Clear top/left styles - el = comp.getEl(); - if (el) { - el.setStyle('top', ''); - el.setStyle(me.horzPositionProp, ''); - } - } - }, - //---------------------------------- - // Misc - regionMeta: { - center: { - splitterDelta: 0 - }, - north: { - splitterDelta: 1 - }, - south: { - splitterDelta: -1 - }, - west: { - splitterDelta: 1 - }, - east: { - splitterDelta: -1 - } - }, - /** - * Flags and configs that get set of regions based on their `region` property. - * @private - */ - regionFlags: { - center: { - isCenter: true, - isHorz: false, - isVert: false - }, - north: { - isCenter: false, - isHorz: false, - isVert: true, - collapseDirection: 'top' - }, - south: { - isCenter: false, - isHorz: false, - isVert: true, - collapseDirection: 'bottom' - }, - west: { - isCenter: false, - isHorz: true, - isVert: false, - collapseDirection: 'left' - }, - east: { - isCenter: false, - isHorz: true, - isVert: false, - collapseDirection: 'right' - } - }, - setupSplitterNeighbors: function(items) { - var edgeRegions = {}, - //north: null, - //south: null, - //east: null, - //west: null - length = items.length, - touchedRegions = this.touchedRegions, - i, j, center, count, edge, comp, region, splitter, touched; - for (i = 0; i < length; ++i) { - comp = items[i].target; - region = comp.region; - if (comp.isCenter) { - center = comp; - } else if (region) { - touched = touchedRegions[region]; - for (j = 0 , count = touched.length; j < count; ++j) { - edge = edgeRegions[touched[j]]; - if (edge) { - edge.neighbors.push(comp); - } - } - if (comp.placeholderFor) { - // placeholder, so grab the splitter for the actual panel - splitter = comp.placeholderFor.splitter; - } else { - splitter = comp.splitter; - } - if (splitter) { - splitter.neighbors = []; - } - edgeRegions[region] = splitter; - } - } - if (center) { - touched = touchedRegions.center; - for (j = 0 , count = touched.length; j < count; ++j) { - edge = edgeRegions[touched[j]]; - if (edge) { - edge.neighbors.push(center); - } - } - } - }, - /** - * Lists the regions that would consider an interior region a neighbor. For example, - * a north region would consider an east or west region its neighbords (as well as - * an inner north region). - * @private - */ - touchedRegions: { - center: [ - 'north', - 'south', - 'east', - 'west' - ], - north: [ - 'north', - 'east', - 'west' - ], - south: [ - 'south', - 'east', - 'west' - ], - east: [ - 'east', - 'north', - 'south' - ], - west: [ - 'west', - 'north', - 'south' - ] - }, - sizePolicies: { - vert: { - readsWidth: 0, - readsHeight: 1, - setsWidth: 1, - setsHeight: 0 - }, - horz: { - readsWidth: 1, - readsHeight: 0, - setsWidth: 0, - setsHeight: 1 - }, - flexAll: { - readsWidth: 0, - readsHeight: 0, - setsWidth: 1, - setsHeight: 1 - } - }, - getItemSizePolicy: function(item) { - var me = this, - policies = this.sizePolicies, - collapseTarget, size, policy, placeholderFor; - if (item.isCenter) { - placeholderFor = item.placeholderFor; - if (placeholderFor) { - if (placeholderFor.collapsedVertical()) { - return policies.vert; - } - return policies.horz; - } - if (item.collapsed) { - if (item.collapsedVertical()) { - return policies.vert; - } - return policies.horz; - } - return policies.flexAll; - } - collapseTarget = item.collapseTarget; - if (collapseTarget) { - return collapseTarget.isVert ? policies.vert : policies.horz; - } - if (item.region) { - if (item.isVert) { - size = item.height; - policy = policies.vert; - } else { - size = item.width; - policy = policies.horz; - } - if (item.flex || (typeof size === 'string' && me.percentageRe.test(size))) { - return policies.flexAll; - } - return policy; - } - return me.autoSizePolicy; - } -}, function() { - var methods = { - addUnflexed: function(px) { - this.flexSpace = Math.max(this.flexSpace - px, 0); - } - }, - props = this.prototype.axisProps; - Ext.apply(props.horz, methods); - Ext.apply(props.vert, methods); -}); - -/** - * This layout manages multiple child Components, each fitted to the Container, where only a single child Component can be - * visible at any given time. This layout style is most commonly used for wizards, tab implementations, etc. - * This class is intended to be extended or created via the layout:'card' {@link Ext.container.Container#layout} config, - * and should generally not need to be created directly via the new keyword. - * - * The CardLayout's focal method is {@link #setActiveItem}. Since only one panel is displayed at a time, - * the only way to move from one Component to the next is by calling setActiveItem, passing the next panel to display - * (or its id or index). The layout itself does not provide a user interface for handling this navigation, - * so that functionality must be provided by the developer. - * - * To change the active card of a container, call the setActiveItem method of its layout: - * - * @example - * var p = Ext.create('Ext.panel.Panel', { - * layout: 'card', - * items: [ - * { html: 'Card 1' }, - * { html: 'Card 2' } - * ], - * renderTo: Ext.getBody() - * }); - * - * p.getLayout().setActiveItem(1); - * - * The {@link Ext.Component#beforedeactivate beforedeactivate} and {@link Ext.Component#beforeactivate beforeactivate} - * events can be used to prevent a card from activating or deactivating by returning `false`. - * - * @example - * var active = 0; - * var main = Ext.create('Ext.panel.Panel', { - * renderTo: Ext.getBody(), - * width: 200, - * height: 200, - * layout: 'card', - * tbar: [{ - * text: 'Next', - * handler: function(){ - * var layout = main.getLayout(); - * ++active; - * layout.setActiveItem(active); - * active = main.items.indexOf(layout.getActiveItem()); - * } - * }], - * items: [{ - * title: 'P1' - * }, { - * title: 'P2' - * }, { - * title: 'P3', - * listeners: { - * beforeactivate: function(){ - * return false; - * } - * } - * }] - * }); - * - * In the following example, a simplistic wizard setup is demonstrated. A button bar is added - * to the footer of the containing panel to provide navigation buttons. The buttons will be handled by a - * common navigation routine. Note that other uses of a CardLayout (like a tab control) would require a - * completely different implementation. For serious implementations, a better approach would be to extend - * CardLayout to provide the custom functionality needed. - * - * @example - * var navigate = function(panel, direction){ - * // This routine could contain business logic required to manage the navigation steps. - * // It would call setActiveItem as needed, manage navigation button state, handle any - * // branching logic that might be required, handle alternate actions like cancellation - * // or finalization, etc. A complete wizard implementation could get pretty - * // sophisticated depending on the complexity required, and should probably be - * // done as a subclass of CardLayout in a real-world implementation. - * var layout = panel.getLayout(); - * layout[direction](); - * Ext.getCmp('move-prev').setDisabled(!layout.getPrev()); - * Ext.getCmp('move-next').setDisabled(!layout.getNext()); - * }; - * - * Ext.create('Ext.panel.Panel', { - * title: 'Example Wizard', - * width: 300, - * height: 200, - * layout: 'card', - * bodyStyle: 'padding:15px', - * defaults: { - * // applied to each contained panel - * border: false - * }, - * // just an example of one possible navigation scheme, using buttons - * bbar: [ - * { - * id: 'move-prev', - * text: 'Back', - * handler: function(btn) { - * navigate(btn.up("panel"), "prev"); - * }, - * disabled: true - * }, - * '->', // greedy spacer so that the buttons are aligned to each side - * { - * id: 'move-next', - * text: 'Next', - * handler: function(btn) { - * navigate(btn.up("panel"), "next"); - * } - * } - * ], - * // the panels (or "cards") within the layout - * items: [{ - * id: 'card-0', - * html: '

    Welcome to the Wizard!

    Step 1 of 3

    ' - * },{ - * id: 'card-1', - * html: '

    Step 2 of 3

    ' - * },{ - * id: 'card-2', - * html: '

    Congratulations!

    Step 3 of 3 - Complete

    ' - * }], - * renderTo: Ext.getBody() - * }); - */ -Ext.define('Ext.layout.container.Card', { - /* Begin Definitions */ - extend: 'Ext.layout.container.Fit', - alternateClassName: 'Ext.layout.CardLayout', - alias: 'layout.card', - /* End Definitions */ - type: 'card', - hideInactive: true, - /** - * @cfg {Boolean} deferredRender - * True to render each contained item at the time it becomes active, false to render all contained items - * as soon as the layout is rendered (defaults to false). If there is a significant amount of content or - * a lot of heavy controls being rendered into panels that are not displayed by default, setting this to - * true might improve performance. - */ - deferredRender: false, - getRenderTree: function() { - var me = this, - activeItem = me.getActiveItem(); - if (activeItem) { - // If they veto the activate, we have no active item - if (activeItem.hasListeners.beforeactivate && activeItem.fireEvent('beforeactivate', activeItem) === false) { - // We must null our activeItem reference, AND the one in our owning Container. - // Because upon layout invalidation, renderChildren will use this.getActiveItem which - // uses this.activeItem || this.owner.activeItem - activeItem = me.activeItem = me.owner.activeItem = null; - } - // Item is to be the active one. Fire event after it is first layed out - else if (activeItem.hasListeners.activate) { - activeItem.on({ - boxready: function() { - activeItem.fireEvent('activate', activeItem); - }, - single: true - }); - } - if (me.deferredRender) { - if (activeItem) { - return me.getItemsRenderTree([ - activeItem - ]); - } - } else { - return me.callParent(arguments); - } - } - }, - renderChildren: function() { - var me = this, - active = me.getActiveItem(); - if (!me.deferredRender) { - me.callParent(); - } else if (active) { - // ensure the active item is configured for the layout - me.renderItems([ - active - ], me.getRenderTarget()); - } - }, - isValidParent: function(item, target, position) { - // Note: Card layout does not care about order within the target because only one is ever visible. - // We only care whether the item is a direct child of the target. - var itemEl = item.el ? item.el.dom : Ext.getDom(item); - return (itemEl && itemEl.parentNode === (target.dom || target)) || false; - }, - /** - * Return the active (visible) component in the layout. - * @return {Ext.Component} - */ - getActiveItem: function() { - var me = this, - // It's necessary to check that me.activeItem is not undefined as it could be 0 (falsey). We're more interested in - // checking the layout's activeItem property, since that is the source of truth for an activeItem. If it's - // determined to be empty, check the owner. Note that a default item is returned if activeItem is `undefined` but - // not `null`. Also, note that `null` is legitimate value and completely different from `undefined`. - item = me.activeItem === undefined ? (me.owner && me.owner.activeItem) : me.activeItem, - result = me.parseActiveItem(item); - // Sanitize the result in case the active item is no longer there. - if (result && me.owner.items.indexOf(result) !== -1) { - me.activeItem = result; - } - // Note that in every use case me.activeItem will have a truthy value except for when a container or tabpanel is explicity - // configured with activeItem/Tab === null or when an out-of-range index is given for an active tab (as it will be undefined). - // In those cases, it is meaningful to return the null value, so do so. - return result == null ? null : (me.activeItem || me.owner.activeItem); - }, - /** - * @private - */ - parseActiveItem: function(item) { - var activeItem; - if (item && item.isComponent) { - activeItem = item; - } else if (typeof item === 'number' || item === undefined) { - activeItem = this.getLayoutItems()[item || 0]; - } else if (item === null) { - activeItem = null; - } else { - activeItem = this.owner.getComponent(item); - } - return activeItem; - }, - /** - * @private - * Called before both dynamic render, and bulk render. - * Ensure that the active item starts visible, and inactive ones start invisible. - */ - configureItem: function(item) { - item.setHiddenState(item !== this.getActiveItem()); - this.callParent(arguments); - }, - onAdd: function(item, pos) { - this.callParent([ - item, - pos - ]); - this.setItemHideMode(item); - }, - onRemove: function(component) { - var me = this; - me.callParent([ - component - ]); - me.resetItemHideMode(component); - if (component === me.activeItem) { - // Note setting to `undefined` is intentional. Don't null it out since null now has a specific meaning in - // tab management (it specifies not setting an active item). - me.activeItem = undefined; - } - }, - /** - * @private - */ - getAnimation: function(newCard, owner) { - var newAnim = (newCard || {}).cardSwitchAnimation; - if (newAnim === false) { - return false; - } - return newAnim || owner.cardSwitchAnimation; - }, - /** - * Return the active (visible) component in the layout to the next card - * @return {Ext.Component} The next component or false. - */ - getNext: function() { - var wrap = arguments[0], - items = this.getLayoutItems(), - index = Ext.Array.indexOf(items, this.activeItem); - return items[index + 1] || (wrap ? items[0] : false); - }, - /** - * Sets the active (visible) component in the layout to the next card - * @return {Ext.Component} the activated component or false when nothing activated. - */ - next: function() { - var anim = arguments[0], - wrap = arguments[1]; - return this.setActiveItem(this.getNext(wrap), anim); - }, - /** - * Return the active (visible) component in the layout to the previous card - * @return {Ext.Component} The previous component or false. - */ - getPrev: function() { - var wrap = arguments[0], - items = this.getLayoutItems(), - index = Ext.Array.indexOf(items, this.activeItem); - return items[index - 1] || (wrap ? items[items.length - 1] : false); - }, - /** - * Sets the active (visible) component in the layout to the previous card - * @return {Ext.Component} the activated component or false when nothing activated. - */ - prev: function() { - var anim = arguments[0], - wrap = arguments[1]; - return this.setActiveItem(this.getPrev(wrap), anim); - }, - /** - * Makes the given card active. - * - * var card1 = Ext.create('Ext.panel.Panel', {itemId: 'card-1'}); - * var card2 = Ext.create('Ext.panel.Panel', {itemId: 'card-2'}); - * var panel = Ext.create('Ext.panel.Panel', { - * layout: 'card', - * activeItem: 0, - * items: [card1, card2] - * }); - * // These are all equivalent - * panel.getLayout().setActiveItem(card2); - * panel.getLayout().setActiveItem('card-2'); - * panel.getLayout().setActiveItem(1); - * - * @param {Ext.Component/Number/String} newCard The component, component {@link Ext.Component#id id}, - * {@link Ext.Component#itemId itemId}, or index of component. - * @return {Ext.Component} the activated component or false when nothing activated. - * False is returned also when trying to activate an already active card. - */ - setActiveItem: function(newCard) { - var me = this, - owner = me.owner, - oldCard = me.activeItem, - rendered = owner.rendered, - newIndex, focusNewCard; - newCard = me.parseActiveItem(newCard); - newIndex = owner.items.indexOf(newCard); - // If the card is not a child of the owner, then add it. - // Without doing a layout! - if (newIndex === -1) { - newIndex = owner.items.items.length; - Ext.suspendLayouts(); - newCard = owner.add(newCard); - Ext.resumeLayouts(); - } - // Is this a valid, different card? - if (newCard && oldCard !== newCard) { - // Fire the beforeactivate and beforedeactivate events on the cards - if (newCard.fireEvent('beforeactivate', newCard, oldCard) === false) { - return false; - } - if (oldCard && oldCard.fireEvent('beforedeactivate', oldCard, newCard) === false) { - return false; - } - if (rendered) { - Ext.suspendLayouts(); - // If the card has not been rendered yet, now is the time to do so. - if (!newCard.rendered) { - me.renderItem(newCard, me.getRenderTarget(), owner.items.length); - } - if (oldCard) { - if (me.hideInactive) { - focusNewCard = oldCard.el.contains(Ext.Element.getActiveElement()); - oldCard.hide(); - if (oldCard.hidden) { - oldCard.hiddenByLayout = true; - oldCard.fireEvent('deactivate', oldCard, newCard); - } else // Hide was vetoed, we cannot change cards. - { - return false; - } - } - } - // Make sure the new card is shown - if (newCard.hidden) { - newCard.show(); - } - // Layout needs activeItem to be correct, so clear it if the show has been vetoed, - // set it if the show has *not* been vetoed. - if (newCard.hidden) { - me.activeItem = newCard = null; - } else { - me.activeItem = newCard; - // If the card being hidden contained focus, attempt to focus the new card - // So as not to leave focus undefined. - // The focus() call will focus the defaultFocus if it is a container - // so ensure there is a defaultFocus. - if (focusNewCard) { - if (!newCard.defaultFocus) { - newCard.defaultFocus = ':focusable'; - } - newCard.focus(); - } - } - Ext.resumeLayouts(true); - } else { - me.activeItem = newCard; - } - newCard.fireEvent('activate', newCard, oldCard); - return me.activeItem; - } - return false; - }, - /** - * @private - * Reset back to initial config when item is removed from the panel. - */ - resetItemHideMode: function(item) { - item.hideMode = item.originalHideMode; - delete item.originalHideMode; - }, - /** - * @private - * A card layout items must have its visibility mode set to OFFSETS so its scroll - * positions isn't reset when hidden. - * - * Do this automatically when an item is added to the panel. - */ - setItemHideMode: function(item) { - item.originalHideMode = item.hideMode; - item.hideMode = 'offsets'; - } -}); - -/** - * This layout manager is used to center contents within a container. As a subclass of - * {@link Ext.layout.container.Fit fit layout}, CenterLayout expects to have one child - * item; multiple items will be placed overlapping. The layout does not require any config - * options. Items in the container can use percentage width or height rather than be fit - * to the full size of the container. - * - * Example usage: - * - * // The content panel is centered in the container - * - * var p = Ext.create('Ext.Panel', { - * title: 'Center Layout', - * layout: 'center', - * items: [{ - * title: 'Centered Content', - * width: '75%', // assign 75% of the container width to the item - * html: 'Some content' - * }] - * }); - * - * If you leave the title blank and specify no border you can create a non-visual, structural - * container just for centering the contents. - * - * var p = Ext.create('Ext.Container', { - * layout: 'center', - * items: [{ - * title: 'Centered Content', - * width: 300, - * height: '90%', // assign 90% of the container height to the item - * html: 'Some content' - * }] - * }); - */ -Ext.define('Ext.layout.container.Center', { - extend: 'Ext.layout.container.Fit', - alias: [ - 'layout.center', - 'layout.ux.center' - ], - alternateClassName: 'Ext.ux.layout.Center', - type: 'center', - percentRe: /^\d+(?:\.\d+)?\%$/, - itemCls: Ext.baseCSSPrefix + 'center-layout-item', - childEls: [ - 'targetEl' - ], - renderTpl: [ - '' - ], - targetElCls: Ext.baseCSSPrefix + 'center-target', - beginLayout: function(ownerContext) { - var me = this, - percentRe = me.percentRe, - childItems, len, i, itemContext, item, widthModel, heightModel; - me.callParent([ - ownerContext - ]); - childItems = ownerContext.childItems; - for (i = 0 , len = childItems.length; i < len; ++i) { - itemContext = childItems[i]; - item = itemContext.target; - widthModel = itemContext.widthModel; - heightModel = itemContext.heightModel; - if (percentRe.test(item.width)) { - item.getEl().setStyle('width', ''); - } - if (percentRe.test(item.height)) { - item.getEl().setStyle('height', ''); - } - } - ownerContext.targetElContext = ownerContext.getEl('targetEl', me); - }, - beginLayoutCycle: function(ownerContext, firstCycle) { - var targetEl = this.targetEl; - this.callParent([ - ownerContext, - firstCycle - ]); - targetEl.setStyle('width', ''); - targetEl.setStyle('height', ''); - }, - getRenderData: function() { - var data = this.callParent(); - data.targetElCls = this.targetElCls; - return data; - }, - getRenderTarget: function() { - return this.targetEl; - }, - getItemSizePolicy: function(item, ownerSizeModel) { - var me = this, - sizeModel = ownerSizeModel || me.owner.getSizeModel(), - percentRe = me.percentRe, - mode = ((sizeModel.width.shrinkWrap || !percentRe.test(item.width)) ? 0 : 1) | // jshint ignore:line - ((sizeModel.height.shrinkWrap || !percentRe.test(item.height)) ? 0 : 2); - return me.sizePolicies[mode]; - }, - isItemBoxParent: function(itemContext) { - return true; - }, - isItemShrinkWrap: function(item) { - return true; - }, - calculate: function(ownerContext) { - var targetElContext = ownerContext.targetElContext, - info; - this.callParent([ - ownerContext - ]); - info = ownerContext.state.info; - if (ownerContext.widthModel.shrinkWrap) { - targetElContext.setWidth(info.contentWidth); - } - if (ownerContext.heightModel.shrinkWrap) { - targetElContext.setHeight(info.contentHeight); - } - }, - getPos: function(itemContext, info, dimension) { - var modelName = dimension + 'Model', - size = itemContext.props[dimension], - pos = 0; - if (!itemContext[modelName].calculated) { - size += info.margins[dimension]; - } - if (!info.ownerContext[modelName].shrinkWrap) { - pos = Math.round((info.targetSize[dimension] - size) / 2); - if (isNaN(pos)) { - this.done = false; - } - } - return Math.max(pos, 0); - }, - positionItemX: function(itemContext, info) { - var left = this.getPos(itemContext, info, 'width'); - itemContext.setProp('x', left); - }, - positionItemY: function(itemContext, info) { - var top = this.getPos(itemContext, info, 'height'); - itemContext.setProp('y', top); - }, - setItemHeight: function(itemContext, info) { - var ratio = parseFloat(itemContext.target.height) / 100; - itemContext.setHeight(Math.round((info.targetSize.height - info.margins.height) * ratio)); - }, - setItemWidth: function(itemContext, info) { - var ratio = parseFloat(itemContext.target.width) / 100; - itemContext.setWidth(Math.round((info.targetSize.width - info.margins.width) * ratio)); - } -}); - -/** - * This is a layout that will render form Fields, one under the other all stretched to the Container width. - * - * @example - * Ext.create('Ext.Panel', { - * width: 500, - * height: 300, - * title: "FormLayout Panel", - * layout: 'form', - * renderTo: Ext.getBody(), - * bodyPadding: 5, - * defaultType: 'textfield', - * items: [{ - * fieldLabel: 'First Name', - * name: 'first', - * allowBlank:false - * },{ - * fieldLabel: 'Last Name', - * name: 'last' - * },{ - * fieldLabel: 'Company', - * name: 'company' - * }, { - * fieldLabel: 'Email', - * name: 'email', - * vtype:'email' - * }, { - * fieldLabel: 'DOB', - * name: 'dob', - * xtype: 'datefield' - * }, { - * fieldLabel: 'Age', - * name: 'age', - * xtype: 'numberfield', - * minValue: 0, - * maxValue: 100 - * }, { - * xtype: 'timefield', - * fieldLabel: 'Time', - * name: 'time', - * minValue: '8:00am', - * maxValue: '6:00pm' - * }] - * }); - */ -Ext.define('Ext.layout.container.Form', { - extend: 'Ext.layout.container.Auto', - alternateClassName: 'Ext.layout.FormLayout', - alias: 'layout.form', - type: 'form', - formWrapCls: Ext.baseCSSPrefix + 'form-layout-wrap', - formWrapAutoLabelCls: Ext.baseCSSPrefix + 'form-layout-auto-label', - formWrapSizedLabelCls: Ext.baseCSSPrefix + 'form-layout-sized-label', - formColGroupCls: Ext.baseCSSPrefix + 'form-layout-colgroup', - formColumnCls: Ext.baseCSSPrefix + 'form-layout-column', - formLabelColumnCls: Ext.baseCSSPrefix + 'form-layout-label-column', - /** - * @cfg {Number} itemSpacing - * The amount of space, in pixels, to use between the items. Defaults to the value - * inherited from the theme's stylesheet as configured by - * {@link Ext.form.Labelable#$form-item-margin-bottom $form-item-margin-bottom}. - */ - /** - * @cfg {Number/String} labelWidth - * The width of the labels. This can be either a number in pixels, or a valid CSS - * "width" style, e.g. `'100px'`, or `'30%'`. When configured, all labels will assume - * this width, and any {@link Ext.form.Labelable#labelWidth labelWidth} specified - * on the items will be ignored. - * - * The default behavior of this layout when no no labelWidth is specified is to size - * the labels to the text-width of the label with the longest text. - */ - childEls: [ - 'formWrap', - 'labelColumn' - ], - beforeBodyTpl: '
    style="border-spacing:{itemSpacing}px">' + '
    ' + '
    style="width:{labelWidth}">' + '
    ' + '
    ' + '
    ', - afterBodyTpl: '
    ', - getRenderData: function() { - var me = this, - labelWidth = me.labelWidth, - formWrapCls = me.formWrapCls, - data = me.callParent(); - if (labelWidth) { - if (typeof labelWidth === 'number') { - labelWidth += 'px'; - } - data.labelWidth = labelWidth; - formWrapCls += ' ' + me.formWrapSizedLabelCls; - } else { - formWrapCls += ' ' + me.formWrapAutoLabelCls; - } - data.formWrapCls = formWrapCls; - data.formColGroupCls = me.formColGroupCls; - data.formColumnCls = me.formColumnCls; - data.formLabelColumnCls = me.formLabelColumnCls; - return data; - }, - getRenderTarget: function() { - return this.formWrap; - } -}); - -/** - * A menu containing a Ext.picker.Color Component. - * - * Notes: - * - * - Although not listed here, the **constructor** for this class accepts all of the - * configuration options of {@link Ext.picker.Color}. - * - If subclassing ColorMenu, any configuration options for the ColorPicker must be - * applied to the **initialConfig** property of the ColorMenu. Applying - * {@link Ext.picker.Color ColorPicker} configuration settings to `this` will **not** - * affect the ColorPicker's configuration. - * - * Example: - * - * @example - * var colorPicker = Ext.create('Ext.menu.ColorPicker', { - * value: '000000' - * }); - * - * Ext.create('Ext.menu.Menu', { - * items: [{ - * text: 'Choose a color', - * menu: colorPicker - * },{ - * iconCls: 'add16', - * text: 'Icon item' - * },{ - * text: 'Regular item' - * }] - * }).showAt([5, 5]); - */ -Ext.define('Ext.menu.ColorPicker', { - extend: 'Ext.menu.Menu', - alias: 'widget.colormenu', - requires: [ - 'Ext.picker.Color' - ], - /** - * @cfg {Boolean} hideOnClick - * False to continue showing the menu after a color is selected. - */ - hideOnClick: true, - /** - * @cfg {String} pickerId - * An id to assign to the underlying color picker. - */ - pickerId: null, - /** - * @cfg {Number} maxHeight - * @private - */ - /** - * @property {Ext.picker.Color} picker - * The {@link Ext.picker.Color} instance for this ColorMenu - */ - /** - * @event click - * @private - */ - initComponent: function() { - var me = this, - cfg = Ext.apply({}, me.initialConfig); - // Ensure we don't get duplicate listeners - delete cfg.listeners; - Ext.apply(me, { - plain: true, - showSeparator: false, - bodyPadding: 0, - items: Ext.applyIf({ - cls: Ext.baseCSSPrefix + 'menu-color-item', - margin: 0, - id: me.pickerId, - xtype: 'colorpicker' - }, cfg) - }); - me.callParent(arguments); - me.picker = me.down('colorpicker'); - /** - * @event select - * @inheritdoc Ext.picker.Color#select - */ - me.relayEvents(me.picker, [ - 'select' - ]); - if (me.hideOnClick) { - me.on('select', me.hidePickerOnSelect, me); - } - }, - /** - * Hides picker on select if hideOnClick is true - * @private - */ - hidePickerOnSelect: function() { - Ext.menu.Manager.hideAll(); - } -}); - -/** - * A menu containing an Ext.picker.Date Component. - * - * Notes: - * - * - Although not listed here, the **constructor** for this class accepts all of the - * configuration options of **{@link Ext.picker.Date}**. - * - If subclassing DateMenu, any configuration options for the DatePicker must be applied - * to the **initialConfig** property of the DateMenu. Applying {@link Ext.picker.Date Date Picker} - * configuration settings to **this** will **not** affect the Date Picker's configuration. - * - * Example: - * - * @example - * var dateMenu = Ext.create('Ext.menu.DatePicker', { - * handler: function(dp, date){ - * Ext.Msg.alert('Date Selected', 'You selected ' + Ext.Date.format(date, 'M j, Y')); - * } - * }); - * - * Ext.create('Ext.menu.Menu', { - * items: [{ - * text: 'Choose a date', - * menu: dateMenu - * },{ - * iconCls: 'add16', - * text: 'Icon item' - * },{ - * text: 'Regular item' - * }] - * }).showAt([5, 5]); - */ -Ext.define('Ext.menu.DatePicker', { - extend: 'Ext.menu.Menu', - alias: 'widget.datemenu', - requires: [ - 'Ext.picker.Date' - ], - ariaRole: 'dialog', - // - /** - * @cfg {String} ariaLabel ARIA label for the Date Picker menu - */ - ariaLabel: 'Date picker', - // - /** - * @cfg {Boolean} hideOnClick - * False to continue showing the menu after a date is selected. - */ - hideOnClick: true, - /** - * @cfg {String} pickerId - * An id to assign to the underlying date picker. - */ - pickerId: null, - /** - * @cfg {Object} [pickerCfg] Date picker configuration. This config - * takes priority over {@link #pickerId}. - - /** - * @cfg {Number} maxHeight - * @private - */ - /** - * @property {Ext.picker.Date} picker - * The {@link Ext.picker.Date} instance for this DateMenu - */ - // DatePicker menu is a special case; Date picker does all key handling - // except the Esc key which is also handled unlike the ordinary menu - enableFocusableContainer: false, - initComponent: function() { - var me = this, - cfg, pickerConfig; - if (me.pickerCfg) { - pickerConfig = Ext.apply({ - cls: Ext.baseCSSPrefix + 'menu-date-item', - margin: 0, - border: false, - id: me.pickerId, - xtype: 'datepicker' - }, me.pickerCfg); - } else { - // Need to keep this insanity for backwards compat :( - cfg = Ext.apply({}, me.initialConfig); - // Ensure we clear any listeners so they aren't duplicated - delete cfg.listeners; - pickerConfig = Ext.applyIf({ - cls: Ext.baseCSSPrefix + 'menu-date-item', - margin: 0, - border: false, - id: me.pickerId, - xtype: 'datepicker' - }, cfg); - } - Ext.apply(me, { - showSeparator: false, - plain: true, - bodyPadding: 0, - // remove the body padding from the datepicker menu item so it looks like 3.3 - items: [ - pickerConfig - ] - }); - me.callParent(); - me.picker = me.down('datepicker'); - /** - * @event select - * @inheritdoc Ext.picker.Date#select - */ - me.relayEvents(me.picker, [ - 'select' - ]); - if (me.hideOnClick) { - me.on('select', me.hidePickerOnSelect, me); - } - }, - onEscapeKey: function(e) { - // Unlike the other menus, DatePicker menu should not close completely on Esc key. - // This is because ordinary menu items will allow using Left arrow key to return - // to the parent menu; however in the Date picker left arrow is used to navigate - // in the calendar. So we use Esc key to return to the parent menu instead. - if (this.floating && this.ownerCmp && this.ownerCmp.focus) { - this.ownerCmp.focus(); - } - }, - hidePickerOnSelect: function() { - Ext.menu.Manager.hideAll(); - } -}); - -/** - * This mixin is applied to panels that want to manage a Pin state and corresponding tool. - */ -Ext.define('Ext.panel.Pinnable', { - extend: 'Ext.Mixin', - mixinId: 'pinnable', - pinnable: true, - pinnedTip: 'Unpin this item', - unpinnedTip: 'Pin this item', - initPinnable: function() { - var me = this, - pinned = me.isPinned(); - me.addTool(me.pinTool = Ext.widget({ - xtype: 'tool', - type: pinned ? 'unpin' : 'pin', - callback: 'togglePin', - scope: me, - tooltip: pinned ? me.pinnedTip : me.unpinnedTip - })); - }, - isPinned: function() { - return !this.floating; - }, - setPinned: function(pinned) { - var me = this, - args; - if (pinned !== me.isPinned()) { - args = [ - me, - pinned - ]; - if (me.fireEventArgs('beforepinchange', args) !== false) { - me.updatePinned(pinned); - me.fireEventArgs('pinchange', args); - } - } - }, - togglePin: function() { - this.setPinned(!this.isPinned()); - }, - updatePinned: function(pinned) { - var me = this, - tool = me.pinTool; - tool.setTooltip(pinned ? me.pinnedTip : me.unpinnedTip); - tool.setType(pinned ? 'unpin' : 'pin'); - } -}); - -/** - * Creates plugin instances. - * - * A plugin may be specified simply as a *config object* as long as the correct `ptype` is specified: - * - * { - * ptype: 'gridviewdragdrop', - * dragText: 'Drag and drop to reorganize' - * } - * - * Or just use the ptype on its own: - * - * 'gridviewdragdrop' - * - * Alternatively you can instantiate the plugin with Ext.create: - * - * Ext.create('Ext.grid.plugin.DragDrop', { - * dragText: 'Drag and drop to reorganize' - * }) - * @private - */ -Ext.define('Ext.plugin.Manager', { - alternateClassName: [ - 'Ext.PluginManager', - 'Ext.PluginMgr' - ], - singleton: true, - typeName: 'ptype', - /** - * Creates a new Plugin from the specified config object using the config object's ptype to determine the class to - * instantiate. - * @param {Object} config A configuration object for the Plugin you wish to create. - * @param {Function} defaultType (optional) The constructor to provide the default Plugin type if the config object does not - * contain a `ptype`. (Optional if the config contains a `ptype`). - * @return {Ext.Component} The newly instantiated Plugin. - */ - create: function(config, defaultType, host) { - var result, type; - if (config.init) { - result = config; - } else { - // Inject the host into the config is we know the host - if (host) { - config = Ext.apply({}, config); - // copy since we are going to modify - config.cmp = host; - } else // Grab the host ref if it was configured in - { - host = config.cmp; - } - if (config.xclass) { - result = Ext.create(config); - } else { - // Lookup the class from the ptype and instantiate unless its a singleton - type = 'plugin.' + (config.ptype || defaultType); - result = Ext.ClassManager.instantiateByAlias(type, config); - } - } - // If we come out with a non-null plugin, ensure that any setCmp is called once. - if (result && host && result.setCmp && !result.setCmpCalled) { - result.setCmp(host); - result.setCmpCalled = true; - } - return result; - } -}); - -/** - * Private utility class for Ext.BorderSplitter. - * @private - */ -Ext.define('Ext.resizer.BorderSplitterTracker', { - extend: 'Ext.resizer.SplitterTracker', - requires: [ - 'Ext.util.Region' - ], - getPrevCmp: null, - getNextCmp: null, - // calculate the constrain Region in which the splitter el may be moved. - calculateConstrainRegion: function() { - var me = this, - splitter = me.splitter, - collapseTarget = splitter.collapseTarget, - defaultSplitMin = splitter.defaultSplitMin, - sizePropCap = splitter.vertical ? 'Width' : 'Height', - minSizeProp = 'min' + sizePropCap, - maxSizeProp = 'max' + sizePropCap, - getSizeMethod = 'get' + sizePropCap, - neighbors = splitter.neighbors, - length = neighbors.length, - box = collapseTarget.el.getBox(), - left = box.x, - top = box.y, - right = box.right, - bottom = box.bottom, - size = splitter.vertical ? (right - left) : (bottom - top), - //neighborSizes = [], - i, neighbor, neighborMaxSize, minRange, maxRange, maxGrowth, maxShrink, targetSize; - // if size=100 and minSize=80, we can reduce by 20 so minRange = minSize-size = -20 - minRange = (collapseTarget[minSizeProp] || Math.min(size, defaultSplitMin)) - size; - // if maxSize=150, maxRange = maxSize - size = 50 - maxRange = collapseTarget[maxSizeProp]; - if (!maxRange) { - maxRange = 1000000000; - } else { - maxRange -= size; - } - targetSize = size; - for (i = 0; i < length; ++i) { - neighbor = neighbors[i]; - size = neighbor[getSizeMethod](); - neighborMaxSize = neighbor[maxSizeProp]; - if (neighborMaxSize === null) { - // calculations that follow expect NaN as a result if max size is undefined - // e.g. (10 - undefined) returns NaN - // Unfortunately the same is not true for null - (10 - null === 10) so - // we convert null into undefined to make sure they both behave the same - neighborMaxSize = undefined; - } - maxGrowth = size - neighborMaxSize; - // NaN if no maxSize or negative - maxShrink = size - (neighbor[minSizeProp] || Math.min(size, defaultSplitMin)); - if (!isNaN(maxGrowth)) { - // if neighbor can only grow by 10 (maxGrowth = -10), minRange cannot be - // -20 anymore, but now only -10: - if (minRange < maxGrowth) { - minRange = maxGrowth; - } - } - // if neighbor can shrink by 20 (maxShrink=20), maxRange cannot be 50 anymore, - // but now only 20: - if (maxRange > maxShrink) { - maxRange = maxShrink; - } - } - if (maxRange - minRange < 2) { - return null; - } - box = new Ext.util.Region(top, right, bottom, left); - me.constraintAdjusters[me.getCollapseDirection()](box, minRange, maxRange, splitter); - me.dragInfo = { - minRange: minRange, - maxRange: maxRange, - //neighborSizes: neighborSizes, - targetSize: targetSize - }; - return box; - }, - constraintAdjusters: { - // splitter is to the right of the box - left: function(box, minRange, maxRange, splitter) { - box[0] = box.x = box.left = box.right + minRange; - box.right += maxRange + splitter.getWidth(); - }, - // splitter is below the box - top: function(box, minRange, maxRange, splitter) { - box[1] = box.y = box.top = box.bottom + minRange; - box.bottom += maxRange + splitter.getHeight(); - }, - // splitter is above the box - bottom: function(box, minRange, maxRange, splitter) { - box.bottom = box.top - minRange; - box.top -= maxRange + splitter.getHeight(); - }, - // splitter is to the left of the box - right: function(box, minRange, maxRange, splitter) { - box.right = box.left - minRange; - box[0] = box.x = box.left = box.x - maxRange + splitter.getWidth(); - } - }, - onBeforeStart: function(e) { - var me = this, - splitter = me.splitter, - collapseTarget = splitter.collapseTarget, - neighbors = splitter.neighbors, - length = neighbors.length, - i, neighbor; - if (collapseTarget.collapsed) { - return false; - } - // disabled if any neighbors are collapsed in parallel direction. - for (i = 0; i < length; ++i) { - neighbor = neighbors[i]; - if (neighbor.collapsed && neighbor.isHorz === collapseTarget.isHorz) { - return false; - } - } - if (!(me.constrainTo = me.calculateConstrainRegion())) { - return false; - } - return true; - }, - performResize: function(e, offset) { - var me = this, - splitter = me.splitter, - collapseDirection = splitter.getCollapseDirection(), - collapseTarget = splitter.collapseTarget, - // a vertical splitter adjusts horizontal dimensions - adjusters = me.splitAdjusters[splitter.vertical ? 'horz' : 'vert'], - delta = offset[adjusters.index], - dragInfo = me.dragInfo, - //neighbors = splitter.neighbors, - //length = neighbors.length, - //neighborSizes = dragInfo.neighborSizes, - //isVert = collapseTarget.isVert, - //i, neighbor, - owner; - if (collapseDirection === 'right' || collapseDirection === 'bottom') { - // these splitters grow by moving left/up, so flip the sign of delta... - delta = -delta; - } - // now constrain delta to our computed range: - delta = Math.min(Math.max(dragInfo.minRange, delta), dragInfo.maxRange); - if (delta) { - (owner = splitter.ownerCt).suspendLayouts(); - adjusters.adjustTarget(collapseTarget, dragInfo.targetSize, delta); - //for (i = 0; i < length; ++i) { - // neighbor = neighbors[i]; - // if (!neighbor.isCenter && !neighbor.maintainFlex && neighbor.isVert == isVert) { - // delete neighbor.flex; - // adjusters.adjustNeighbor(neighbor, neighborSizes[i], delta); - // } - //} - owner.resumeLayouts(true); - } - }, - splitAdjusters: { - horz: { - index: 0, - //adjustNeighbor: function (neighbor, size, delta) { - // neighbor.setSize(size - delta); - //}, - adjustTarget: function(target, size, delta) { - target.flex = null; - target.setSize(size + delta); - } - }, - vert: { - index: 1, - //adjustNeighbor: function (neighbor, size, delta) { - // neighbor.setSize(undefined, size - delta); - //}, - adjustTarget: function(target, targetSize, delta) { - target.flex = null; - target.setSize(undefined, targetSize + delta); - } - } - }, - getCollapseDirection: function() { - return this.splitter.getCollapseDirection(); - } -}); - -/** - * Provides a handle for 9-point resizing of Elements or Components. - */ -Ext.define('Ext.resizer.Handle', { - extend: 'Ext.Component', - handleCls: '', - baseHandleCls: Ext.baseCSSPrefix + 'resizable-handle', - // Ext.resizer.Resizer.prototype.possiblePositions define the regions - // which will be passed in as a region configuration. - region: '', - ariaRole: 'presentation', - beforeRender: function() { - var me = this; - me.callParent(); - me.protoEl.unselectable(); - me.addCls(me.baseHandleCls, me.baseHandleCls + '-' + me.region, me.handleCls); - } -}); - -/** - * Private utility class for Ext.resizer.Resizer. - * @private - */ -Ext.define('Ext.resizer.ResizeTracker', { - extend: 'Ext.dd.DragTracker', - dynamic: true, - preserveRatio: false, - // Default to no constraint - constrainTo: null, - proxyCls: Ext.baseCSSPrefix + 'resizable-proxy', - constructor: function(config) { - var me = this, - widthRatio, heightRatio, throttledResizeFn; - if (!config.el) { - if (config.target.isComponent) { - me.el = config.target.getEl(); - } else { - me.el = config.target; - } - } - this.callParent(arguments); - // Ensure that if we are preserving aspect ratio, the largest minimum is honoured - if (me.preserveRatio && me.minWidth && me.minHeight) { - widthRatio = me.minWidth / me.el.getWidth(); - heightRatio = me.minHeight / me.el.getHeight(); - // largest ratio of minimum:size must be preserved. - // So if a 400x200 pixel image has - // minWidth: 50, maxWidth: 50, the maxWidth will be 400 * (50/200)... that is 100 - if (heightRatio > widthRatio) { - me.minWidth = me.el.getWidth() * heightRatio; - } else { - me.minHeight = me.el.getHeight() * widthRatio; - } - } - // If configured as throttled, create an instance version of resize which calls - // a throttled function to perform the resize operation. - if (me.throttle) { - throttledResizeFn = Ext.Function.createThrottled(function() { - Ext.resizer.ResizeTracker.prototype.resize.apply(me, arguments); - }, me.throttle); - me.resize = function(box, direction, atEnd) { - if (atEnd) { - Ext.resizer.ResizeTracker.prototype.resize.apply(me, arguments); - } else { - throttledResizeFn.apply(null, arguments); - } - }; - } - }, - onBeforeStart: function(e) { - // record the startBox - this.startBox = this.target.getBox(); - }, - /** - * @private - * Returns the object that will be resized instead of the true target on every mousemove event. - * If dynamic is false, this will be a proxy, otherwise it will be null target. - */ - getProxy: function() { - var me = this; - if (!me.dynamic && !me.proxy) { - me.proxy = me.createProxy(me.target || me.el); - // Only hide proxy at end if we create one dynamically - // When a wrapped resizer is used it passes the wrapping el in as the proxy. - me.hideProxy = true; - } - if (me.proxy) { - me.proxy.show(); - return me.proxy; - } - }, - /** - * Create a proxy for this resizer - * @param {Ext.Component/Ext.dom.Element} target The target - * @return {Ext.dom.Element} A proxy element - */ - createProxy: function(target) { - var proxy, - cls = this.proxyCls; - if (target.isComponent) { - proxy = target.getProxy().addCls(cls); - } else { - proxy = target.createProxy({ - tag: 'div', - role: 'presentation', - cls: cls, - id: target.id + '-rzproxy' - }, Ext.getBody()); - } - proxy.removeCls(Ext.baseCSSPrefix + 'proxy-el'); - return proxy; - }, - onStart: function(e) { - // returns the Ext.ResizeHandle that the user started dragging - this.activeResizeHandle = Ext.get(this.getDragTarget().id); - // If we are using a proxy, ensure it is sized. - if (!this.dynamic) { - this.resize(this.startBox); - } - }, - onMouseDown: function(e, target) { - // Logic to resize components on top of iframes. To properly handle iframes "below" - // the resizable component, we cannot wait for triggerStart or onStart because if - // mouse moves out of the component and over the iframe we won't detect that the - // drag has started. So we have to mask the iframes now. We cannot do this in - // general because other draggable things cannot assume that mouseDown is safe - // for this purpose. In particular ComponentDragger on a maximaizable window will - // get tricked by the maximize button onMouseDown and mask everything but will - // noever get the onMopuseUp to unmask. - var targetEl = Ext.fly(target.parentNode); - this.callParent(arguments); - if (targetEl && targetEl.shim) { - targetEl.maskIframes(); - } - }, - onMouseUp: function(e) { - var targetEl = Ext.fly(this.dragTarget.parentNode); - this.callParent(arguments); - if (targetEl && targetEl.shim) { - targetEl.unmaskIframes(); - } - }, - onDrag: function(e) { - // dynamic resizing, update dimensions during resize - if (this.dynamic || this.proxy) { - this.updateDimensions(e); - } - }, - updateDimensions: function(e, atEnd) { - var me = this, - region = me.activeResizeHandle.region, - offset = me.getOffset(me.constrainTo ? 'dragTarget' : null), - box = me.startBox, - ratio, - widthAdjust = 0, - heightAdjust = 0, - snappedWidth, snappedHeight, - adjustX = 0, - adjustY = 0, - dragRatio, oppositeCorner, axis, // 1 = x, 2 = y, 3 = x and y. - newBox, newHeight, newWidth; - region = me.convertRegionName(region); - switch (region) { - case 'south': - heightAdjust = offset[1]; - axis = 2; - break; - case 'north': - heightAdjust = -offset[1]; - adjustY = -heightAdjust; - axis = 2; - break; - case 'east': - widthAdjust = offset[0]; - axis = 1; - break; - case 'west': - widthAdjust = -offset[0]; - adjustX = -widthAdjust; - axis = 1; - break; - case 'northeast': - heightAdjust = -offset[1]; - adjustY = -heightAdjust; - widthAdjust = offset[0]; - oppositeCorner = [ - box.x, - box.y + box.height - ]; - axis = 3; - break; - case 'southeast': - heightAdjust = offset[1]; - widthAdjust = offset[0]; - oppositeCorner = [ - box.x, - box.y - ]; - axis = 3; - break; - case 'southwest': - widthAdjust = -offset[0]; - adjustX = -widthAdjust; - heightAdjust = offset[1]; - oppositeCorner = [ - box.x + box.width, - box.y - ]; - axis = 3; - break; - case 'northwest': - heightAdjust = -offset[1]; - adjustY = -heightAdjust; - widthAdjust = -offset[0]; - adjustX = -widthAdjust; - oppositeCorner = [ - box.x + box.width, - box.y + box.height - ]; - axis = 3; - break; - } - newBox = { - width: box.width + widthAdjust, - height: box.height + heightAdjust, - x: box.x + adjustX, - y: box.y + adjustY - }; - // Snap value between stops according to configured increments - snappedWidth = Ext.Number.snap(newBox.width, me.widthIncrement); - snappedHeight = Ext.Number.snap(newBox.height, me.heightIncrement); - if (snappedWidth !== newBox.width || snappedHeight !== newBox.height) { - switch (region) { - case 'northeast': - newBox.y -= snappedHeight - newBox.height; - break; - case 'north': - newBox.y -= snappedHeight - newBox.height; - break; - case 'southwest': - newBox.x -= snappedWidth - newBox.width; - break; - case 'west': - newBox.x -= snappedWidth - newBox.width; - break; - case 'northwest': - newBox.x -= snappedWidth - newBox.width; - newBox.y -= snappedHeight - newBox.height; - } - newBox.width = snappedWidth; - newBox.height = snappedHeight; - } - // out of bounds - if (newBox.width < me.minWidth || newBox.width > me.maxWidth) { - newBox.width = Ext.Number.constrain(newBox.width, me.minWidth, me.maxWidth); - // Re-adjust the X position if we were dragging the west side - if (adjustX) { - newBox.x = box.x + (box.width - newBox.width); - } - } else { - me.lastX = newBox.x; - } - if (newBox.height < me.minHeight || newBox.height > me.maxHeight) { - newBox.height = Ext.Number.constrain(newBox.height, me.minHeight, me.maxHeight); - // Re-adjust the Y position if we were dragging the north side - if (adjustY) { - newBox.y = box.y + (box.height - newBox.height); - } - } else { - me.lastY = newBox.y; - } - // If this is configured to preserve the aspect ratio, or they are dragging using the shift key - if (me.preserveRatio || e.shiftKey) { - ratio = me.startBox.width / me.startBox.height; - // Calculate aspect ratio constrained values. - newHeight = Math.min(Math.max(me.minHeight, newBox.width / ratio), me.maxHeight); - newWidth = Math.min(Math.max(me.minWidth, newBox.height * ratio), me.maxWidth); - // X axis: width-only change, height must obey - if (axis === 1) { - newBox.height = newHeight; - } - // Y axis: height-only change, width must obey - else if (axis === 2) { - newBox.width = newWidth; - } else // Corner drag. - { - // Drag ratio is the ratio of the mouse point from the opposite corner. - // Basically what edge we are dragging, a horizontal edge or a vertical edge. - dragRatio = Math.abs(oppositeCorner[0] - this.lastXY[0]) / Math.abs(oppositeCorner[1] - this.lastXY[1]); - // If drag ratio > aspect ratio then width is dominant and height must obey - if (dragRatio > ratio) { - newBox.height = newHeight; - } else { - newBox.width = newWidth; - } - // Handle dragging start coordinates - if (region === 'northeast') { - newBox.y = box.y - (newBox.height - box.height); - } else if (region === 'northwest') { - newBox.y = box.y - (newBox.height - box.height); - newBox.x = box.x - (newBox.width - box.width); - } else if (region === 'southwest') { - newBox.x = box.x - (newBox.width - box.width); - } - } - } - // Keep track of whether position needs changing - me.setPosition = newBox.x !== me.startBox.x || newBox.y !== me.startBox.y; - me.resize(newBox, atEnd); - }, - resize: function(box, atEnd) { - var me = this, - target, - setPosition = me.setPosition; - // We are live resizing the target, or at the end: Size the target - if (me.dynamic || (!me.dynamic && atEnd)) { - // Resize the target - if (setPosition) { - me.target.setBox(box); - } else { - me.target.setSize(box.width, box.height); - } - } - // In the middle of a resize - just resize the proxy - if (!atEnd) { - target = me.getProxy(); - if (target && target !== me.target) { - if (setPosition || me.hideProxy) { - target.setBox(box); - } else { - target.setSize(box.width, box.height); - } - } - } - }, - onEnd: function(e) { - this.updateDimensions(e, true); - if (this.proxy && this.hideProxy) { - this.proxy.hide(); - } - }, - convertRegionName: function(name) { - return name; - } -}); - -/** - * Applies drag handles to an element or component to make it resizable. The drag handles are inserted into the element - * (or component's element) and positioned absolute. - * - * Textarea and img elements will be wrapped with an additional div because these elements do not support child nodes. - * The original element can be accessed through the originalTarget property. - * - * Here is the list of valid resize handles: - * - * Value Description - * ------ ------------------- - * 'n' north - * 's' south - * 'e' east - * 'w' west - * 'nw' northwest - * 'sw' southwest - * 'se' southeast - * 'ne' northeast - * 'all' all - * - * {@img Ext.resizer.Resizer/Ext.resizer.Resizer.png Ext.resizer.Resizer component} - * - * Here's an example showing the creation of a typical Resizer: - * - * Ext.create('Ext.resizer.Resizer', { - * target: 'elToResize', - * handles: 'all', - * minWidth: 200, - * minHeight: 100, - * maxWidth: 500, - * maxHeight: 400, - * pinned: true - * }); - */ -Ext.define('Ext.resizer.Resizer', { - mixins: { - observable: 'Ext.util.Observable' - }, - uses: [ - 'Ext.resizer.ResizeTracker', - 'Ext.Component' - ], - alternateClassName: 'Ext.Resizable', - handleCls: Ext.baseCSSPrefix + 'resizable-handle', - overCls: Ext.baseCSSPrefix + 'resizable-handle-over', - pinnedCls: Ext.baseCSSPrefix + 'resizable-pinned', - wrapCls: Ext.baseCSSPrefix + 'resizable-wrap', - wrappedCls: Ext.baseCSSPrefix + 'resizable-wrapped', - delimiterRe: /(?:\s*[,;]\s*)|\s+/, - /** - * @cfg {Boolean} dynamic - * Specify as true to update the {@link #target} (Element or {@link Ext.Component Component}) dynamically during - * dragging. This is `true` by default, but the {@link Ext.Component Component} class passes `false` when it is - * configured as {@link Ext.Component#resizable}. - * - * If specified as `false`, a proxy element is displayed during the resize operation, and the {@link #target} is - * updated on mouseup. - */ - dynamic: true, - /** - * @cfg {String} handles - * String consisting of the resize handles to display. Defaults to 's e se' for Elements and fixed position - * Components. Defaults to 8 point resizing for floating Components (such as Windows). Specify either `'all'` or any - * of `'n s e w ne nw se sw'`. - */ - handles: 's e se', - /** - * @cfg {Number} height - * Optional. The height to set target to in pixels - */ - height: null, - /** - * @cfg {Number} width - * Optional. The width to set the target to in pixels - */ - width: null, - /** - * @cfg {Number} heightIncrement - * The increment to snap the height resize in pixels. - */ - heightIncrement: 0, - /** - * @cfg {Number} widthIncrement - * The increment to snap the width resize in pixels. - */ - widthIncrement: 0, - /** - * @cfg {Number} minHeight - * The minimum height for the element - */ - minHeight: 20, - /** - * @cfg {Number} minWidth - * The minimum width for the element - */ - minWidth: 20, - /** - * @cfg {Number} maxHeight - * The maximum height for the element - */ - maxHeight: 10000, - /** - * @cfg {Number} maxWidth - * The maximum width for the element - */ - maxWidth: 10000, - /** - * @cfg {Boolean} pinned - * True to ensure that the resize handles are always visible, false indicates resizing by cursor changes only - */ - pinned: false, - /** - * @cfg {Boolean} preserveRatio - * True to preserve the original ratio between height and width during resize - */ - preserveRatio: false, - /** - * @cfg {Boolean} transparent - * True for transparent handles. This is only applied at config time. - */ - transparent: false, - /** - * @cfg {Ext.dom.Element/Ext.util.Region} constrainTo - * An element, or a {@link Ext.util.Region Region} into which the resize operation must be constrained. - */ - possiblePositions: { - n: 'north', - s: 'south', - e: 'east', - w: 'west', - se: 'southeast', - sw: 'southwest', - nw: 'northwest', - ne: 'northeast' - }, - /** - * @cfg {Ext.dom.Element/Ext.Component} target - * The Element or Component to resize. - */ - /** - * @property {Ext.dom.Element} el - * Outer element for resizing behavior. - */ - ariaRole: 'presentation', - /** - * @event beforeresize - * Fired before resize is allowed. Return false to cancel resize. - * @param {Ext.resizer.Resizer} this - * @param {Number} width The start width - * @param {Number} height The start height - * @param {Ext.event.Event} e The mousedown event - */ - /** - * @event resizedrag - * Fires during resizing. - * @param {Ext.resizer.Resizer} this - * @param {Number} width The new width - * @param {Number} height The new height - * @param {Ext.event.Event} e The mousedown event - */ - /** - * @event resize - * Fired after a resize. - * @param {Ext.resizer.Resizer} this - * @param {Number} width The new width - * @param {Number} height The new height - * @param {Ext.event.Event} e The mouseup event - */ - constructor: function(config) { - var me = this, - handles = me.handles, - unselectableCls = Ext.dom.Element.unselectableCls, - handleEls = [], - resizeTarget, handleCls, possibles, tag, len, i, pos, el, box, wrapTarget, positioning, targetBaseCls; - if (Ext.isString(config) || Ext.isElement(config) || config.dom) { - resizeTarget = config; - config = arguments[1] || {}; - config.target = resizeTarget; - } - // will apply config to this - me.mixins.observable.constructor.call(me, config); - // If target is a Component, ensure that we pull the element out. - // Resizer must examine the underlying Element. - resizeTarget = me.target; - if (resizeTarget) { - if (resizeTarget.isComponent) { - // Resizable Components get a new UI class on them which makes them overflow:visible - // if the border width is non-zero and therefore the SASS has embedded the handles - // in the borders using -ve position. - resizeTarget.addClsWithUI('resizable'); - if (resizeTarget.minWidth) { - me.minWidth = resizeTarget.minWidth; - } - if (resizeTarget.minHeight) { - me.minHeight = resizeTarget.minHeight; - } - if (resizeTarget.maxWidth) { - me.maxWidth = resizeTarget.maxWidth; - } - if (resizeTarget.maxHeight) { - me.maxHeight = resizeTarget.maxHeight; - } - if (resizeTarget.floating) { - if (!me.hasOwnProperty('handles')) { - me.handles = 'n ne e se s sw w nw'; - } - } - me.el = resizeTarget.getEl(); - } else { - resizeTarget = me.el = me.target = Ext.get(resizeTarget); - } - } else // Backwards compatibility with Ext3.x's Resizable which used el as a config. - { - resizeTarget = me.target = me.el = Ext.get(me.el); - } - // Locally enforce border box model. - // https://sencha.jira.com/browse/EXTJSIV-11511 - me.el.addCls(Ext.Component.prototype.borderBoxCls); - // Constrain within configured maxima - if (Ext.isNumber(me.width)) { - me.width = Ext.Number.constrain(me.width, me.minWidth, me.maxWidth); - } - if (Ext.isNumber(me.height)) { - me.height = Ext.Number.constrain(me.height, me.minHeight, me.maxHeight); - } - // Size the target. - if (me.width !== null || me.height !== null) { - me.target.setSize(me.width, me.height); - } - // Tags like textarea and img cannot - // have children and therefore must - // be wrapped - tag = me.el.dom.tagName.toUpperCase(); - if (tag === 'TEXTAREA' || tag === 'IMG' || tag === 'TABLE') { - /** - * @property {Ext.dom.Element/Ext.Component} originalTarget - * Reference to the original resize target if the element of the original resize target was a - * {@link Ext.form.field.Field Field}, or an IMG or a TEXTAREA which must be wrapped in a DIV. - */ - me.originalTarget = me.target; - wrapTarget = resizeTarget.isComponent ? resizeTarget.getEl() : resizeTarget; - // Tag the wrapped element with a class so thaht we can force it to use border box sizing model - me.el.addCls(me.wrappedCls); - me.target = me.el = me.el.wrap({ - role: 'presentation', - cls: me.wrapCls, - id: me.el.id + '-rzwrap', - style: wrapTarget.getStyle([ - 'margin-top', - 'margin-bottom' - ]) - }); - positioning = wrapTarget.getPositioning(); - // Transfer originalTarget's positioning+sizing+margins - me.el.setPositioning(positioning); - wrapTarget.clearPositioning(); - box = wrapTarget.getBox(); - if (positioning.position !== 'absolute') { - //reset coordinates - box.x = 0; - box.y = 0; - } - me.el.setBox(box); - // Position the wrapped element absolute so that it does not stretch the wrapper - wrapTarget.setStyle('position', 'absolute'); - me.isTargetWrapped = true; - } - // Position the element, this enables us to absolute position - // the handles within this.el - me.el.position(); - if (me.pinned) { - me.el.addCls(me.pinnedCls); - } - /** - * @property {Ext.resizer.ResizeTracker} resizeTracker - */ - me.resizeTracker = new Ext.resizer.ResizeTracker({ - disabled: me.disabled, - target: resizeTarget, - el: me.el, - constrainTo: me.constrainTo, - handleCls: me.handleCls, - overCls: me.overCls, - throttle: me.throttle, - // If we have wrapped something, instruct the ResizerTracker to use that wrapper as a proxy - // and we should resize the wrapped target dynamically. - proxy: me.originalTarget ? me.el : null, - dynamic: me.originalTarget ? true : me.dynamic, - originalTarget: me.originalTarget, - delegate: '.' + me.handleCls, - preserveRatio: me.preserveRatio, - heightIncrement: me.heightIncrement, - widthIncrement: me.widthIncrement, - minHeight: me.minHeight, - maxHeight: me.maxHeight, - minWidth: me.minWidth, - maxWidth: me.maxWidth - }); - // Relay the ResizeTracker's superclass events as our own resize events - me.resizeTracker.on({ - mousedown: me.onBeforeResize, - drag: me.onResize, - dragend: me.onResizeEnd, - scope: me - }); - if (me.handles === 'all') { - me.handles = 'n s e w ne nw se sw'; - } - handles = me.handles = me.handles.split(me.delimiterRe); - possibles = me.possiblePositions; - len = handles.length; - handleCls = me.handleCls + ' ' + me.handleCls + '-{0}'; - if (me.target.isComponent) { - targetBaseCls = me.target.baseCls; - handleCls += ' ' + targetBaseCls + '-handle ' + targetBaseCls + '-handle-{0}'; - if (Ext.supports.CSS3BorderRadius) { - handleCls += ' ' + targetBaseCls + '-handle-{0}-br'; - } - } - for (i = 0; i < len; i++) { - // if specified and possible, create - if (handles[i] && possibles[handles[i]]) { - pos = possibles[handles[i]]; - handleEls.push(''); - } - } - Ext.DomHelper.append(me.el, handleEls.join('')); - // Let's reuse the handleEls stack to collect the actual els. - handleEls.length = 0; - // store a reference to each handle element in this.east, this.west, etc - for (i = 0; i < len; i++) { - // if specified and possible, create - if (handles[i] && possibles[handles[i]]) { - pos = possibles[handles[i]]; - el = me[pos] = me.el.getById(me.el.id + '-' + pos + '-handle'); - handleEls.push(el); - el.region = pos; - if (me.transparent) { - el.setOpacity(0); - } - } - } - me.resizeTracker.handleEls = handleEls; - }, - disable: function() { - this.resizeTracker.disable(); - }, - enable: function() { - this.resizeTracker.enable(); - }, - /** - * @private - * Relay the Tracker's mousedown event as beforeresize - * @param {Ext.resizer.ResizeTracker} tracker - * @param {Ext.event.Event} e The event - */ - onBeforeResize: function(tracker, e) { - return this.fireResizeEvent('beforeresize', tracker, e); - }, - /** - * @private - * Relay the Tracker's drag event as resizedrag - * @param {Ext.resizer.ResizeTracker} tracker - * @param {Ext.event.Event} e The event - */ - onResize: function(tracker, e) { - return this.fireResizeEvent('resizedrag', tracker, e); - }, - /** - * @private - * Relay the Tracker's dragend event as resize - * @param {Ext.resizer.ResizeTracker} tracker - * @param {Ext.event.Event} e The event - */ - onResizeEnd: function(tracker, e) { - return this.fireResizeEvent('resize', tracker, e); - }, - /** - * @private - * Fire a resize event, checking if we have listeners before firing. - * @param {String} name The name of the event - * @param {Ext.resizer.ResizeTracker} tracker - * @param {Ext.event.Event} e The event - */ - fireResizeEvent: function(name, tracker, e) { - var me = this, - box; - if (me.hasListeners[name]) { - box = me.el.getBox(); - return me.fireEvent(name, me, box.width, box.height, e); - } - }, - /** - * Perform a manual resize and fires the 'resize' event. - * @param {Number} width - * @param {Number} height - */ - resizeTo: function(width, height) { - var me = this; - me.target.setSize(width, height); - me.fireEvent('resize', me, width, height, null); - }, - /** - * Returns the element that was configured with the el or target config property. If a component was configured with - * the target property then this will return the element of this component. - * - * Textarea and img elements will be wrapped with an additional div because these elements do not support child - * nodes. The original element can be accessed through the originalTarget property. - * @return {Ext.dom.Element} element - */ - getEl: function() { - return this.el; - }, - /** - * Returns the element or component that was configured with the target config property. - * - * Textarea and img elements will be wrapped with an additional div because these elements do not support child - * nodes. The original element can be accessed through the originalTarget property. - * @return {Ext.dom.Element/Ext.Component} - */ - getTarget: function() { - return this.target; - }, - destroy: function() { - var me = this, - i, - handles = me.handles, - len = handles.length, - positions = me.possiblePositions, - handle; - me.resizeTracker.destroy(); - // The target is redefined as an element when it's wrapped so we must destroy it. - if (me.isTargetWrapped) { - me.target.destroy(); - } - for (i = 0; i < len; i++) { - if ((handle = me[positions[handles[i]]])) { - handle.destroy(); - } - } - me.callParent(); - } -}); - -/** - * A selection model for {@link Ext.grid.Panel grid panels} which allows selection of a single cell at a time. - * - * Implements cell based navigation via keyboard. - * - * @example - * var store = Ext.create('Ext.data.Store', { - * fields: ['name', 'email', 'phone'], - * data: [ - * { name: 'Lisa', email: 'lisa@simpsons.com', phone: '555-111-1224' }, - * { name: 'Bart', email: 'bart@simpsons.com', phone: '555-222-1234' }, - * { name: 'Homer', email: 'homer@simpsons.com', phone: '555-222-1244' }, - * { name: 'Marge', email: 'marge@simpsons.com', phone: '555-222-1254' } - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Simpsons', - * store: store, - * width: 400, - * renderTo: Ext.getBody(), - * columns: [ - * { text: 'Name', dataIndex: 'name' }, - * { text: 'Email', dataIndex: 'email', flex: 1 }, - * { text: 'Phone', dataIndex: 'phone' } - * ], - * selModel: 'cellmodel' - * }); - */ -Ext.define('Ext.selection.CellModel', { - extend: 'Ext.selection.DataViewModel', - alias: 'selection.cellmodel', - requires: [ - 'Ext.grid.CellContext' - ], - /** - * @cfg {"SINGLE"} mode - * Mode of selection. Valid values are: - * - * - **"SINGLE"** - Only allows selecting one item at a time. This is the default. - */ - isCellModel: true, - /** - * @inheritdoc - */ - deselectOnContainerClick: false, - /** - * @cfg {Boolean} enableKeyNav - * Turns on/off keyboard navigation within the grid. - */ - enableKeyNav: true, - /** - * @cfg {Boolean} preventWrap - * Set this configuration to true to prevent wrapping around of selection as - * a user navigates to the first or last column. - */ - preventWrap: false, - /** - * @event deselect - * Fired after a cell is deselected - * @param {Ext.selection.CellModel} this - * @param {Ext.data.Model} record The record of the deselected cell - * @param {Number} row The row index deselected - * @param {Number} column The column index deselected - */ - /** - * @event select - * Fired after a cell is selected - * @param {Ext.selection.CellModel} this - * @param {Ext.data.Model} record The record of the selected cell - * @param {Number} row The row index selected - * @param {Number} column The column index selected - */ - bindComponent: function(view) { - var me = this, - grid; - // Unbind from a view - if (me.view && me.gridListeners) { - me.gridListeners.destroy(); - } - // DataViewModel's bindComponent - me.callParent([ - view - ]); - if (view) { - // view.grid is present during View construction, before the view has been - // added as a child of the Panel, and an upward link it still needed. - grid = view.grid || view.ownerCt; - if (grid.optimizedColumnMove !== false) { - me.gridListeners = grid.on({ - columnmove: me.onColumnMove, - scope: me, - destroyable: true - }); - } - } - }, - getViewListeners: function() { - var result = this.callParent(); - result.refresh = this.onViewRefresh; - return result; - }, - getHeaderCt: function() { - var selection = this.navigationModel.getPosition(), - view = selection ? selection.view : this.primaryView; - return view.headerCt; - }, - // Selection blindly follows focus. For now. - onNavigate: function(e) { - // It was a navigate out event. - // Or stopSelection was stamped into the event by an upstream handler. - // This is used by ActionColumn and CheckColumn to implement their stopSelection config - if (!e.record || e.keyEvent.stopSelection) { - return; - } - this.setPosition(e.position); - }, - selectWithEvent: function(record, e) { - this.select(record); - }, - /** - * Selects a cell by row / column. - * - * var grid = Ext.create('Ext.grid.Panel', { - * title: 'Simpsons', - * store: { - * fields: ['name', 'email', 'phone'], - * data: [{ - * name: "Lisa", - * email: "lisa@simpsons.com", - * phone: "555-111-1224" - * }] - * }, - * columns: [{ - * text: 'Name', - * dataIndex: 'name' - * }, { - * text: 'Email', - * dataIndex: 'email', - * hidden: true - * }, { - * text: 'Phone', - * dataIndex: 'phone', - * flex: 1 - * }], - * height: 200, - * width: 400, - * renderTo: Ext.getBody(), - * selType: 'cellmodel', - * tbar: [{ - * text: 'Select position Object', - * handler: function() { - * grid.getSelectionModel().select({ - * row: grid.getStore().getAt(0), - * column: grid.down('gridcolumn[dataIndex=name]') - * }); - * } - * }, { - * text: 'Select position by Number', - * handler: function() { - * grid.getSelectionModel().select({ - * row: 0, - * column: 1 - * }); - * } - * }] - * }); - * - * @param {Object} pos An object with row and column properties - * @param {Ext.data.Model/Number} pos.row - * A record or index of the record (starting at 0) - * @param {Ext.grid.column.Column/Number} pos.column - * A column or index of the column (starting at 0). Includes visible columns only. - */ - select: function(pos, /* private */ - keepExisting, suppressEvent) { - var me = this, - row, - oldPos = me.getPosition(), - store = me.view.store; - if (pos || pos === 0) { - if (pos.isModel) { - row = store.indexOf(pos); - if (row !== -1) { - pos = { - row: row, - column: oldPos ? oldPos.column : 0 - }; - } else { - pos = null; - } - } else if (typeof pos === 'number') { - pos = { - row: pos, - column: 0 - }; - } - } - if (pos) { - me.selectByPosition(pos, suppressEvent); - } else { - me.deselect(); - } - }, - /** - * Returns the current position in the format {row: row, column: column} - * @deprecated 5.0.1 This API uses column indices which include hidden columns in the count. Use {@link #getPosition} instead. - */ - getCurrentPosition: function() { - // If it's during a select, return nextSelection since we buffer - // the real selection until after the event fires - var position = this.selecting ? this.nextSelection : this.selection; - // This is the previous Format of the private CellContext class which was used here. - // Do not return a CellContext so that if this object is passed into setCurrentPosition, it will be - // read in the legacy (including hidden columns) way. - return position ? { - view: position.view, - record: position.record, - row: position.rowIdx, - columnHeader: position.column, - // IMPORTANT: The historic API for columns has been to include hidden columns - // in the index. So we must report the index of the column in the "all" ColumnManager. - column: position.view.getColumnManager().indexOf(position.column) - } : position; - }, - /** - * Returns the current position in the format {row: row, column: column} - * @return {Ext.grid.CellContext} A CellContext object describing the current cell. - */ - getPosition: function() { - return (this.selecting ? this.nextSelection : this.selection) || null; - }, - /** - * Sets the current position. - * @deprecated 5.0.1 This API uses column indices which include hidden columns in the count. Use {@link #setPosition} instead. - * @param {Ext.grid.CellContext/Object} position The position to set. May be an object of the form `{row:1, column:2}` - * @param {Boolean} suppressEvent True to suppress selection events - */ - setCurrentPosition: function(pos, suppressEvent, /* private */ - preventCheck) { - if (pos && !pos.isCellContext) { - pos = new Ext.grid.CellContext(this.view).setPosition({ - row: pos.row, - // IMPORTANT: The historic API for columns has been to include hidden columns - // in the index. So we must index into the "all" ColumnManager. - column: typeof pos.column === 'number' ? this.view.getColumnManager().getColumns()[pos.column] : pos.column - }); - } - return this.setPosition(pos, suppressEvent, preventCheck); - }, - /** - * Sets the current position. - * - * Note that if passing a column index, it is the index within the *visible* column set. - * - * @param {Ext.grid.CellContext/Object} position The position to set. May be an object of the form `{row:1, column:2}` - * @param {Boolean} suppressEvent True to suppress selection events - */ - setPosition: function(pos, suppressEvent, /* private */ - preventCheck) { - var me = this, - last = me.selection; - // Normalize it into an Ext.grid.CellContext if necessary - if (pos) { - pos = pos.isCellContext ? pos.clone() : new Ext.grid.CellContext(me.view).setPosition(pos); - } - if (!preventCheck && last) { - // If the position is the same, jump out & don't fire the event - if (pos && (pos.record === last.record && pos.column === last.column && pos.view === last.view)) { - pos = null; - } else { - me.onCellDeselect(me.selection, suppressEvent); - } - } - if (pos) { - me.nextSelection = pos; - // set this flag here so we know to use nextSelection - // if the node is updated during a select - me.selecting = true; - me.onCellSelect(me.nextSelection, suppressEvent); - me.selecting = false; - // Deselect triggered by new selection will kill the selection property, so restore it here. - return (me.selection = pos); - } - // Enforce code correctness in unbuilt source. - return null; - }, - isCellSelected: function(view, row, column) { - var me = this, - testPos, - pos = me.getPosition(); - if (pos && pos.view === view) { - testPos = new Ext.grid.CellContext(view).setPosition({ - row: row, - // IMPORTANT: The historic API for columns has been to include hidden columns - // in the index. So we must index into the "all" ColumnManager. - column: typeof column === 'number' ? view.getColumnManager().getColumns()[column] : column - }); - return (testPos.record === pos.record) && (testPos.column === pos.column); - } - }, - // Keep selection model in consistent state upon record deletion. - onStoreRemove: function(store, records, indices) { - var me = this, - pos = me.getPosition(); - me.callParent(arguments); - if (pos && store.isMoving(pos.record)) { - return; - } - if (pos && store.getCount() && store.indexOf(pos.record) !== -1) { - pos.setRow(pos.record); - } else { - me.selection = null; - } - }, - onStoreClear: function() { - this.callParent(arguments); - this.selection = null; - }, - onStoreAdd: function() { - var me = this, - pos = me.getPosition(); - me.callParent(arguments); - if (pos) { - pos.setRow(pos.record); - } else { - me.selection = null; - } - }, - /** - * Set the current position based on where the user clicks. - * @private - * IMPORTANT* Due to V4.0.0 history, the cellIndex here is the index within ALL columns, including hidden. - */ - onCellClick: function(view, cell, cellIndex, record, row, recordIndex, e) { - // Record index will be -1 if the clicked record is a metadata record and not selectable - if (recordIndex !== -1) { - this.setPosition(e.position); - } - }, - // notify the view that the cell has been selected to update the ui - // appropriately and bring the cell into focus - onCellSelect: function(position, supressEvent) { - if (position && position.rowIdx !== undefined && position.rowIdx > -1) { - this.doSelect(position.record, /*keepExisting*/ - false, supressEvent); - } - }, - // notify view that the cell has been deselected to update the ui - // appropriately - onCellDeselect: function(position, supressEvent) { - if (position && position.rowIdx !== undefined) { - this.doDeselect(position.record, supressEvent); - } - }, - onSelectChange: function(record, isSelected, suppressEvent, commitFn) { - var me = this, - pos, eventName, view; - if (isSelected) { - pos = me.nextSelection; - eventName = 'select'; - } else { - pos = me.selection; - eventName = 'deselect'; - } - // CellModel may be shared between two sides of a Lockable. - // The position must include a reference to the view in which the selection is current. - // Ensure we use the view specified by the position. - view = pos.view || me.primaryView; - if ((suppressEvent || me.fireEvent('before' + eventName, me, record, pos.rowIdx, pos.colIdx)) !== false && commitFn() !== false) { - if (isSelected) { - view.onCellSelect(pos); - } else { - view.onCellDeselect(pos); - delete me.selection; - } - if (!suppressEvent) { - me.fireEvent(eventName, me, record, pos.rowIdx, pos.colIdx); - } - } - }, - refresh: function() { - var pos = this.getPosition(), - selRowIdx; - // Synchronize the current position's row with the row of the last selected record. - if (pos && (selRowIdx = this.store.indexOf(this.selected.last())) !== -1) { - pos.rowIdx = selRowIdx; - } - }, - /** - * @private - * When grid uses {@link Ext.panel.Table#optimizedColumnMove optimizedColumnMove} (the default), this is added as a - * {@link Ext.panel.Table#columnmove columnmove} handler to correctly maintain the - * selected column using the same column header. - * - * If optimizedColumnMove === false, (which some grid Features set) then the view is refreshed, - * so this is not added as a handler because the selected column. - */ - onColumnMove: function(headerCt, header, fromIdx, toIdx) { - var grid = headerCt.up('tablepanel'); - if (grid) { - this.onViewRefresh(grid.view); - } - }, - onUpdate: function(record) { - var me = this, - pos; - if (me.isSelected(record)) { - pos = me.selecting ? me.nextSelection : me.selection; - me.view.onCellSelect(pos); - } - }, - onViewRefresh: function(view) { - var me = this, - pos = me.getPosition(), - newPos, - headerCt = view.headerCt, - record, column; - // Re-establish selection of the same cell coordinate. - // DO NOT fire events because the selected - if (pos && pos.view === view) { - record = pos.record; - column = pos.column; - // After a refresh, recreate the selection using the same record and grid column as before - if (!column.isDescendantOf(headerCt)) { - // column header is not a child of the header container - // this happens when the grid is reconfigured with new columns - // make a best effor to select something by matching on id, then text, then dataIndex - column = headerCt.queryById(column.id) || headerCt.down('[text="' + column.text + '"]') || headerCt.down('[dataIndex="' + column.dataIndex + '"]'); - } - // If we have a columnHeader (either the column header that already exists in - // the headerCt, or a suitable match that was found after reconfiguration) - // AND the record still exists in the store (or a record matching the id of - // the previously selected record) We are ok to go ahead and set the selection - if (pos.record) { - if (column && (view.store.indexOfId(record.getId()) !== -1)) { - newPos = new Ext.grid.CellContext(view).setPosition({ - row: record, - column: column - }); - me.setPosition(newPos); - } - } else { - me.selection = null; - } - } - }, - /** - * @private - * Used internally by CellEditing - */ - selectByPosition: function(position, suppressEvent) { - this.setPosition(position, suppressEvent); - } -}); - -/** - * RowModel Selection Model implements row based navigation for - * {@link Ext.grid.Panel grid panels} via user input. RowModel is the default grid selection - * model and, generally, will not need to be specified. - * - * By utilizing the selModel config as an object, you may also set configurations for: - * - * + {@link #mode} - Specifies whether user may select multiple rows or single rows - * + {@link #allowDeselect} - Specifies whether user may deselect records when in SINGLE mode - * + {@link #ignoreRightMouseSelection} - Specifies whether user may ignore right clicks - * for selection purposes - * - * In the example below, we've enabled MULTI mode. This means that multiple rows can be selected. - * - * @example - * var store = Ext.create('Ext.data.Store', { - * fields: ['name', 'email', 'phone'], - * data: [ - * { name: 'Lisa', email: 'lisa@simpsons.com', phone: '555-111-1224' }, - * { name: 'Bart', email: 'bart@simpsons.com', phone: '555-222-1234' }, - * { name: 'Homer', email: 'homer@simpsons.com', phone: '555-222-1244' }, - * { name: 'Marge', email: 'marge@simpsons.com', phone: '555-222-1254' } - * ] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Simpsons', - * store: store, - * width: 400, - * renderTo: Ext.getBody(), - * selModel: { - * selType: 'rowmodel', // rowmodel is the default selection model - * mode: 'MULTI' // Allows selection of multiple rows - * }, - * columns: [ - * { text: 'Name', dataIndex: 'name' }, - * { text: 'Email', dataIndex: 'email', flex: 1 }, - * { text: 'Phone', dataIndex: 'phone' } - * ] - * }); - */ -Ext.define('Ext.selection.RowModel', { - extend: 'Ext.selection.DataViewModel', - alias: 'selection.rowmodel', - requires: [ - 'Ext.grid.CellContext' - ], - /** - * @cfg {Boolean} enableKeyNav - * - * Turns on/off keyboard navigation within the grid. - */ - enableKeyNav: true, - /** - * @event beforedeselect - * Fired before a record is deselected. If any listener returns false, the - * deselection is cancelled. - * @param {Ext.selection.RowModel} this - * @param {Ext.data.Model} record The deselected record - * @param {Number} index The row index deselected - */ - /** - * @event beforeselect - * Fired before a record is selected. If any listener returns false, the - * selection is cancelled. - * @param {Ext.selection.RowModel} this - * @param {Ext.data.Model} record The selected record - * @param {Number} index The row index selected - */ - /** - * @event deselect - * Fired after a record is deselected - * @param {Ext.selection.RowModel} this - * @param {Ext.data.Model} record The deselected record - * @param {Number} index The row index deselected - */ - /** - * @event select - * Fired after a record is selected - * @param {Ext.selection.RowModel} this - * @param {Ext.data.Model} record The selected record - * @param {Number} index The row index selected - */ - isRowModel: true, - /** - * @inheritdoc - */ - deselectOnContainerClick: false, - onUpdate: function(record) { - var me = this, - view = me.view, - index; - if (view && me.isSelected(record)) { - index = view.indexOf(record); - view.onRowSelect(index); - if (record === me.lastFocused) { - view.onRowFocus(index, true); - } - } - }, - // Allow the GridView to update the UI by - // adding/removing a CSS class from the row. - onSelectChange: function(record, isSelected, suppressEvent, commitFn) { - var me = this, - views = me.views || [ - me.view - ], - viewsLn = views.length, - recordIndex = me.store.indexOf(record), - eventName = isSelected ? 'select' : 'deselect', - i, view; - if ((suppressEvent || me.fireEvent('before' + eventName, me, record, recordIndex)) !== false && commitFn() !== false) { - // Selection models can handle more than one view - for (i = 0; i < viewsLn; i++) { - view = views[i]; - recordIndex = view.indexOf(record); - // The record might not be rendered due to either buffered rendering, - // or removal/hiding of all columns (eg empty locked side). - if (view.indexOf(record) !== -1) { - if (isSelected) { - view.onRowSelect(recordIndex, suppressEvent); - } else { - view.onRowDeselect(recordIndex, suppressEvent); - } - } - } - if (!suppressEvent) { - me.fireEvent(eventName, me, record, recordIndex); - } - } - }, - /** - * Returns position of the first selected cell in the selection in the format {row: row, column: column} - * @deprecated 5.0.1 Use the {@link Ext.view.Table#getNavigationModel NavigationModel} instead. - */ - getCurrentPosition: function() { - var firstSelection = this.selected.getAt(0); - if (firstSelection) { - return new Ext.grid.CellContext(this.view).setPosition(this.store.indexOf(firstSelection), 0); - } - }, - selectByPosition: function(position, keepExisting) { - if (!position.isCellContext) { - position = new Ext.grid.CellContext(this.view).setPosition(position.row, position.column); - } - this.select(position.record, keepExisting); - }, - /** - * Selects the record immediately following the currently selected record. - * @param {Boolean} [keepExisting] True to retain existing selections - * @param {Boolean} [suppressEvent] Set to false to not fire a select event - * @return {Boolean} `true` if there is a next record, else `false` - */ - selectNext: function(keepExisting, suppressEvent) { - var me = this, - store = me.store, - selection = me.getSelection(), - record = selection[selection.length - 1], - index = me.view.indexOf(record) + 1, - success; - if (index === store.getCount() || index === 0) { - success = false; - } else { - me.doSelect(index, keepExisting, suppressEvent); - success = true; - } - return success; - }, - /** - * Selects the record that precedes the currently selected record. - * @param {Boolean} [keepExisting] True to retain existing selections - * @param {Boolean} [suppressEvent] Set to false to not fire a select event - * @return {Boolean} `true` if there is a previous record, else `false` - */ - selectPrevious: function(keepExisting, suppressEvent) { - var me = this, - selection = me.getSelection(), - record = selection[0], - index = me.view.indexOf(record) - 1, - success; - if (index < 0) { - success = false; - } else { - me.doSelect(index, keepExisting, suppressEvent); - success = true; - } - return success; - }, - isRowSelected: function(record) { - return this.isSelected(record); - }, - isCellSelected: function(view, record, columnHeader) { - return this.isSelected(record); - }, - vetoSelection: function(e) { - var navModel = this.view.getNavigationModel(), - key = e.getKey(), - isLeftRight = key === e.RIGHT || key === e.LEFT; - // Veto row selection upon key-based, in-row left/right navigation. - // Else pass to superclass to veto. - return (isLeftRight && navModel.previousRecord === navModel.record) || this.callParent([ - e - ]); - } -}); - -/** - * A selection model that renders a column of checkboxes that can be toggled to - * select or deselect rows. The default mode for this selection model is MULTI. - * - * @example - * var store = Ext.create('Ext.data.Store', { - * fields: ['name', 'email', 'phone'], - * data: [{ - * name: 'Lisa', - * email: 'lisa@simpsons.com', - * phone: '555-111-1224' - * }, { - * name: 'Bart', - * email: 'bart@simpsons.com', - * phone: '555-222-1234' - * }, { - * name: 'Homer', - * email: 'homer@simpsons.com', - * phone: '555-222-1244' - * }, { - * name: 'Marge', - * email: 'marge@simpsons.com', - * phone: '555-222-1254' - * }] - * }); - * - * Ext.create('Ext.grid.Panel', { - * title: 'Simpsons', - * store: store, - * columns: [{ - * text: 'Name', - * dataIndex: 'name' - * }, { - * text: 'Email', - * dataIndex: 'email', - * flex: 1 - * }, { - * text: 'Phone', - * dataIndex: 'phone' - * }], - * height: 200, - * width: 400, - * renderTo: Ext.getBody(), - * selModel: { - * selType: 'checkboxmodel' - * } - * }); - * - * The selection model will inject a header for the checkboxes in the first view - * and according to the {@link #injectCheckbox} configuration. - */ -Ext.define('Ext.selection.CheckboxModel', { - alias: 'selection.checkboxmodel', - extend: 'Ext.selection.RowModel', - /** - * @cfg {"SINGLE"/"SIMPLE"/"MULTI"} mode - * Modes of selection. - * Valid values are `"SINGLE"`, `"SIMPLE"`, and `"MULTI"`. - */ - mode: 'MULTI', - /** - * @cfg {Number/String} [injectCheckbox=0] - * The index at which to insert the checkbox column. - * Supported values are a numeric index, and the strings 'first' and 'last'. - */ - injectCheckbox: 0, - /** - * @cfg {Boolean} checkOnly - * True if rows can only be selected by clicking on the checkbox column, not by clicking - * on the row itself. Note that this only refers to selection via the UI, programmatic - * selection will still occur regardless. - */ - checkOnly: false, - /** - * @cfg {Boolean} showHeaderCheckbox - * Configure as `false` to not display the header checkbox at the top of the column. - * When the store is a {@link Ext.data.BufferedStore BufferedStore}, this configuration will - * not be available because the buffered data set does not always contain all data. - */ - showHeaderCheckbox: undefined, - /** - * @cfg {String} [checkSelector="x-grid-row-checker"] - * The selector for determining whether the checkbox element is clicked. This may be changed to - * allow for a wider area to be clicked, for example, the whole cell for the selector. - */ - checkSelector: '.' + Ext.baseCSSPrefix + 'grid-row-checker', - allowDeselect: true, - headerWidth: 24, - /** - * @private - */ - checkerOnCls: Ext.baseCSSPrefix + 'grid-hd-checker-on', - tdCls: Ext.baseCSSPrefix + 'grid-cell-special ' + Ext.baseCSSPrefix + 'grid-cell-row-checker', - constructor: function() { - var me = this; - me.callParent(arguments); - // If mode is single and showHeaderCheck isn't explicity set to - // true, hide it. - if (me.mode === 'SINGLE') { - if (me.showHeaderCheckbox) { - Ext.Error.raise('The header checkbox is not supported for SINGLE mode selection models.'); - } - me.showHeaderCheckbox = false; - } - }, - beforeViewRender: function(view) { - var me = this, - owner; - me.callParent(arguments); - // if we have a locked header, only hook up to the first - if (!me.hasLockedHeader() || view.headerCt.lockedCt) { - me.addCheckbox(view, true); - owner = view.ownerCt; - // Listen to the outermost reconfigure event - if (view.headerCt.lockedCt) { - owner = owner.ownerCt; - } - me.mon(owner, 'reconfigure', me.onReconfigure, me); - } - }, - bindComponent: function(view) { - this.sortable = false; - this.callParent(arguments); - }, - hasLockedHeader: function() { - var views = this.views, - vLen = views.length, - v; - for (v = 0; v < vLen; v++) { - if (views[v].headerCt.lockedCt) { - return true; - } - } - return false; - }, - /** - * Add the header checkbox to the header row - * @private - * @param {Boolean} initial True if we're binding for the first time. - */ - addCheckbox: function(view, initial) { - var me = this, - checkbox = me.injectCheckbox, - headerCt = view.headerCt; - // Preserve behaviour of false, but not clear why that would ever be done. - if (checkbox !== false) { - if (checkbox === 'first') { - checkbox = 0; - } else if (checkbox === 'last') { - checkbox = headerCt.getColumnCount(); - } - Ext.suspendLayouts(); - if (view.getStore().isBufferedStore) { - me.showHeaderCheckbox = false; - } - me.column = headerCt.add(checkbox, me.getHeaderConfig()); - Ext.resumeLayouts(); - } - if (initial !== true) { - view.refresh(); - } - }, - /** - * Handles the grid's reconfigure event. Adds the checkbox header if the columns have been reconfigured. - * @private - * @param {Ext.panel.Table} grid - * @param {Ext.data.Store} store - * @param {Object[]} columns - */ - onReconfigure: function(grid, store, columns) { - if (columns) { - this.addCheckbox(this.views[0]); - } - }, - /** - * Toggle the ui header between checked and unchecked state. - * @param {Boolean} isChecked - * @private - */ - toggleUiHeader: function(isChecked) { - var view = this.views[0], - headerCt = view.headerCt, - checkHd = headerCt.child('gridcolumn[isCheckerHd]'), - cls = this.checkerOnCls; - if (checkHd) { - if (isChecked) { - checkHd.addCls(cls); - } else { - checkHd.removeCls(cls); - } - } - }, - /** - * Toggle between selecting all and deselecting all when clicking on - * a checkbox header. - */ - onHeaderClick: function(headerCt, header, e) { - var me = this, - isChecked; - if (header === me.column && me.mode !== 'SINGLE') { - e.stopEvent(); - isChecked = header.el.hasCls(Ext.baseCSSPrefix + 'grid-hd-checker-on'); - if (isChecked) { - me.deselectAll(); - } else { - me.selectAll(); - } - } - }, - /** - * Retrieve a configuration to be used in a HeaderContainer. - * This should be used when injectCheckbox is set to false. - */ - getHeaderConfig: function() { - var me = this, - showCheck = me.showHeaderCheckbox !== false; - return { - xtype: 'gridcolumn', - ignoreExport: true, - isCheckerHd: showCheck, - text: ' ', - clickTargetName: 'el', - width: me.headerWidth, - sortable: false, - draggable: false, - resizable: false, - hideable: false, - menuDisabled: true, - dataIndex: '', - tdCls: me.tdCls, - cls: showCheck ? Ext.baseCSSPrefix + 'column-header-checkbox ' : '', - defaultRenderer: me.renderer.bind(me), - editRenderer: me.editRenderer || me.renderEmpty, - locked: me.hasLockedHeader(), - processEvent: me.processColumnEvent - }; - }, - /** - * @private - * Process and refire events routed from the Ext.panel.Table's processEvent method. - * Also fires any configured click handlers. By default, cancels the mousedown event to prevent selection. - * Returns the event handler's status to allow canceling of GridView's bubbling process. - */ - processColumnEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) { - var navModel = view.getNavigationModel(); - // Fire a navigate event upon SPACE in acvtionable mode. - // SPACE events are ignored by the NavModel in actionable mode. - if (e.type === 'keydown' && view.actionableMode && e.getKey() === e.SPACE) { - navModel.fireEvent('navigate', { - view: view, - navigationModel: navModel, - keyEvent: e, - position: e.position, - recordIndex: recordIndex, - record: record, - item: e.item, - cell: e.position.cellElement, - columnIndex: e.position.colIdx, - column: e.position.column - }); - } - }, - renderEmpty: function() { - return ' '; - }, - // After refresh, ensure that the header checkbox state matches - refresh: function() { - this.callParent(arguments); - this.updateHeaderState(); - }, - /** - * Generates the HTML to be rendered in the injected checkbox column for each row. - * Creates the standard checkbox markup by default; can be overridden to provide custom rendering. - * See {@link Ext.grid.column.Column#renderer} for description of allowed parameters. - */ - renderer: function(value, metaData, record, rowIndex, colIndex, store, view) { - return '
     
    '; - }, - selectByPosition: function(position, keepExisting) { - if (!position.isCellContext) { - position = new Ext.grid.CellContext(this.view).setPosition(position.row, position.column); - } - // Do not select if checkOnly, and the requested position is not the check column - if (!this.checkOnly || position.column === this.column) { - this.callParent([ - position, - keepExisting - ]); - } - }, - /** - * Synchronize header checker value as selection changes. - * @private - */ - onSelectChange: function() { - this.callParent(arguments); - if (!this.suspendChange) { - this.updateHeaderState(); - } - }, - /** - * @private - */ - onStoreLoad: function() { - this.callParent(arguments); - this.updateHeaderState(); - }, - onStoreAdd: function() { - this.callParent(arguments); - this.updateHeaderState(); - }, - onStoreRemove: function() { - this.callParent(arguments); - this.updateHeaderState(); - }, - onStoreRefresh: function() { - this.callParent(arguments); - this.updateHeaderState(); - }, - maybeFireSelectionChange: function(fireEvent) { - if (fireEvent && !this.suspendChange) { - this.updateHeaderState(); - } - this.callParent(arguments); - }, - resumeChanges: function() { - this.callParent(); - if (!this.suspendChange) { - this.updateHeaderState(); - } - }, - /** - * @private - */ - updateHeaderState: function() { - // check to see if all records are selected - var me = this, - store = me.store, - storeCount = store.getCount(), - views = me.views, - hdSelectStatus = false, - selectedCount = 0, - selected, len, i; - if (!store.isBufferedStore && storeCount > 0) { - selected = me.selected; - hdSelectStatus = true; - for (i = 0 , len = selected.getCount(); i < len; ++i) { - if (store.indexOfId(selected.getAt(i).id) === -1) { - break; - } - ++selectedCount; - } - hdSelectStatus = storeCount === selectedCount; - } - if (views && views.length) { - me.toggleUiHeader(hdSelectStatus); - } - }, - vetoSelection: function(e) { - var me = this, - column = me.column, - veto, isClick, isSpace; - if (me.checkOnly) { - isClick = e.type === 'click' && e.getTarget(me.checkSelector); - isSpace = e.getKey() === e.SPACE && e.position.column === column; - veto = !(isClick || isSpace); - } - return veto || me.callParent([ - e - ]); - }, - destroy: function() { - this.column = null; - this.callParent(); - }, - privates: { - onBeforeNavigate: function(metaEvent) { - var e = metaEvent.keyEvent; - if (this.selectionMode !== 'SINGLE') { - metaEvent.ctrlKey = metaEvent.ctrlKey || e.ctrlKey || (e.type === 'click' && !e.shiftKey) || e.getKey() === e.SPACE; - } - }, - selectWithEventMulti: function(record, e, isSelected) { - var me = this; - if (!e.shiftKey && !e.ctrlKey && e.getTarget(me.checkSelector)) { - if (isSelected) { - me.doDeselect(record); - } else { - me.doSelect(record, true); - } - } else { - me.callParent([ - record, - e, - isSelected - ]); - } - } - } -}); - -/** - * This selection model is created by default for {@link Ext.tree.Panel}. - * - * It implements a row selection model. - */ -Ext.define('Ext.selection.TreeModel', { - extend: 'Ext.selection.RowModel', - alias: 'selection.treemodel', - /** - * @cfg {Boolean} pruneRemoved - * @hide - */ - /** - * @cfg {Boolean} selectOnExpanderClick - * `true` to select the row when clicking on the icon to collapse or expand - * a tree node. - * - * @since 5.1.0 - */ - selectOnExpanderClick: false, - constructor: function(config) { - var me = this; - me.callParent([ - config - ]); - // If pruneRemoved is required, we must listen to the the Store's bubbled noderemove event to know when nodes - // are added and removed from parentNodes. - // The Store's remove event will be fired during collapses. - if (me.pruneRemoved) { - me.pruneRemoved = false; - me.pruneRemovedNodes = true; - } - }, - getStoreListeners: function() { - var me = this, - result = me.callParent(); - result.noderemove = me.onNodeRemove; - return result; - }, - onNodeRemove: function(parent, node, isMove) { - // deselection of deleted records done in base Model class - if (!isMove) { - var toDeselect = []; - this.gatherSelected(node, toDeselect); - if (toDeselect.length) { - this.deselect(toDeselect); - } - } - }, - // onStoreRefresh asks if it should remove from the selection any selected records which are no - // longer findable in the store after the refresh. - // TreeModel does not use the pruneRemoved flag because records are being added and removed - // from TreeStores on exand and collapse. It uses the pruneRemovedNodes flag. - pruneRemovedOnRefresh: function() { - return this.pruneRemovedNodes; - }, - vetoSelection: function(e) { - var view = this.view, - select = this.selectOnExpanderClick, - veto = !select && e.type === 'click' && e.getTarget(view.expanderSelector || (view.lockingPartner && view.lockingPartner.expanderSelector)); - return veto || this.callParent([ - e - ]); - }, - privates: { - gatherSelected: function(node, toDeselect) { - var childNodes = node.childNodes, - i, len, child; - if (this.selected.containsKey(node.id)) { - toDeselect.push(node); - } - if (childNodes) { - for (i = 0 , len = childNodes.length; i < len; ++i) { - child = childNodes[i]; - this.gatherSelected(child, toDeselect); - } - } - } - } -}); - -/** - * @class Ext.slider.Thumb - * @private - * Represents a single thumb element on a Slider. This would not usually be created manually and would instead - * be created internally by an {@link Ext.slider.Multi Multi slider}. - */ -Ext.define('Ext.slider.Thumb', { - requires: [ - 'Ext.dd.DragTracker', - 'Ext.util.Format' - ], - overCls: Ext.baseCSSPrefix + 'slider-thumb-over', - /** - * @cfg {Ext.slider.MultiSlider} slider (required) - * The Slider to render to. - */ - /** - * Creates new slider thumb. - * @param {Object} [config] Config object. - */ - constructor: function(config) { - var me = this; - /** - * @property {Ext.slider.MultiSlider} slider - * The slider this thumb is contained within - */ - Ext.apply(me, config || {}, { - cls: Ext.baseCSSPrefix + 'slider-thumb', - /** - * @cfg {Boolean} constrain True to constrain the thumb so that it cannot overlap its siblings - */ - constrain: false - }); - me.callParent([ - config - ]); - }, - /** - * Renders the thumb into a slider - */ - render: function() { - var me = this; - me.el = me.slider.innerEl.insertFirst(me.getElConfig()); - me.onRender(); - }, - onRender: function() { - if (this.disabled) { - this.disable(); - } - this.initEvents(); - }, - getElConfig: function() { - var me = this, - slider = me.slider, - style = {}; - style[slider.vertical ? 'bottom' : slider.horizontalProp] = slider.calculateThumbPosition(slider.normalizeValue(me.value)) + '%'; - return { - style: style, - id: this.id, - cls: this.cls, - role: 'presentation' - }; - }, - /** - * @private - * move the thumb - */ - move: function(v, animate) { - var me = this, - el = me.el, - slider = me.slider, - styleProp = slider.vertical ? 'bottom' : slider.horizontalProp, - to, from, animCfg; - v += '%'; - if (!animate) { - el.dom.style[styleProp] = v; - } else { - to = {}; - to[styleProp] = v; - if (!Ext.supports.GetPositionPercentage) { - from = {}; - from[styleProp] = el.dom.style[styleProp]; - } - // Animation config - animCfg = { - target: el, - duration: 350, - from: from, - to: to, - scope: me, - callback: me.onAnimComplete - }; - if (animate !== true) { - Ext.apply(animCfg, animate); - } - me.anim = new Ext.fx.Anim(animCfg); - } - }, - onAnimComplete: function() { - this.anim = null; - }, - /** - * Enables the thumb if it is currently disabled - */ - enable: function() { - var el = this.el; - this.disabled = false; - if (el) { - el.removeCls(this.slider.disabledCls); - } - }, - /** - * Disables the thumb if it is currently enabled - */ - disable: function() { - var el = this.el; - this.disabled = true; - if (el) { - el.addCls(this.slider.disabledCls); - } - }, - /** - * Sets up an Ext.dd.DragTracker for this thumb - */ - initEvents: function() { - var me = this; - me.tracker = new Ext.dd.DragTracker({ - el: me.el, - onBeforeStart: me.onBeforeDragStart.bind(me), - onStart: me.onDragStart.bind(me), - onDrag: me.onDrag.bind(me), - onEnd: me.onDragEnd.bind(me), - tolerance: 3, - autoStart: 300 - }); - me.el.hover(me.addOverCls, me.removeOverCls, me); - }, - addOverCls: function() { - var me = this; - if (!me.disabled) { - me.el.addCls(me.overCls); - } - }, - removeOverCls: function() { - this.el.removeCls(this.overCls); - }, - /** - * @private - * This is tied into the internal Ext.dd.DragTracker. If the slider is currently disabled, - * this returns false to disable the DragTracker too. - * @return {Boolean} False if the slider is currently disabled - */ - onBeforeDragStart: function(e) { - var me = this, - el = me.el, - trackerXY = me.tracker.getXY(), - delta = me.pointerOffset = el.getXY(); - if (me.disabled) { - return false; - } else { - // Work out the delta of the pointer from the dead centre of the thumb. - // Slider.getTrackPoint positions the centre of the slider at the reported - // pointer position, so we have to correct for that in getValueFromTracker. - delta[0] += Math.floor(el.getWidth() / 2) - trackerXY[0]; - delta[1] += Math.floor(el.getHeight() / 2) - trackerXY[1]; - me.slider.promoteThumb(me); - return true; - } - }, - /** - * @private - * This is tied into the internal Ext.dd.DragTracker's onStart template method. Adds the drag CSS class - * to the thumb and fires the 'dragstart' event - */ - onDragStart: function(e) { - var me = this, - slider = me.slider; - slider.onDragStart(me, e); - me.el.addCls(Ext.baseCSSPrefix + 'slider-thumb-drag'); - me.dragging = me.slider.dragging = true; - me.dragStartValue = me.value; - slider.fireEvent('dragstart', slider, e, me); - }, - /** - * @private - * This is tied into the internal Ext.dd.DragTracker's onDrag template method. This is called every time - * the DragTracker detects a drag movement. It updates the Slider's value using the position of the drag - */ - onDrag: function(e) { - var me = this, - slider = me.slider, - index = me.index, - newValue = me.getValueFromTracker(), - above, below; - // If dragged out of range, value will be undefined - if (newValue !== undefined) { - if (me.constrain) { - above = slider.thumbs[index + 1]; - below = slider.thumbs[index - 1]; - if (below !== undefined && newValue <= below.value) { - newValue = below.value; - } - if (above !== undefined && newValue >= above.value) { - newValue = above.value; - } - } - slider.setValue(index, newValue, false); - slider.fireEvent('drag', slider, e, me); - } - }, - getValueFromTracker: function() { - var slider = this.slider, - trackerXY = this.tracker.getXY(), - trackPoint; - trackerXY[0] += this.pointerOffset[0]; - trackerXY[1] += this.pointerOffset[1]; - trackPoint = slider.getTrackpoint(trackerXY); - // If dragged out of range, value will be undefined - if (trackPoint !== undefined) { - return slider.reversePixelValue(trackPoint); - } - }, - /** - * @private - * This is tied to the internal Ext.dd.DragTracker's onEnd template method. Removes the drag CSS class and - * fires the 'changecomplete' event with the new value - */ - onDragEnd: function(e) { - var me = this, - slider = me.slider, - value = me.value; - slider.onDragEnd(me, e); - me.el.removeCls(Ext.baseCSSPrefix + 'slider-thumb-drag'); - me.dragging = slider.dragging = false; - slider.fireEvent('dragend', slider, e); - if (me.dragStartValue !== value) { - slider.fireEvent('changecomplete', slider, value, me); - } - }, - destroy: function() { - var me = this, - anim = this.anim; - if (anim) { - anim.end(); - } - me.el = me.tracker = me.anim = Ext.destroy(me.el, me.tracker); - me.callParent(); - } -}); - -/** - * Simple plugin for using an Ext.tip.Tip with a slider to show the slider value. In general this class is not created - * directly, instead pass the {@link Ext.slider.Multi#useTips} and {@link Ext.slider.Multi#tipText} configuration - * options to the slider directly. - * - * @example - * Ext.create('Ext.slider.Single', { - * width: 214, - * minValue: 0, - * maxValue: 100, - * useTips: true, - * renderTo: Ext.getBody() - * }); - * - * Optionally provide your own tip text by passing tipText: - * - * @example - * Ext.create('Ext.slider.Single', { - * width: 214, - * minValue: 0, - * maxValue: 100, - * useTips: true, - * tipText: function(thumb){ - * return Ext.String.format('**{0}% complete**', thumb.value); - * }, - * renderTo: Ext.getBody() - * }); - */ -Ext.define('Ext.slider.Tip', { - extend: 'Ext.tip.Tip', - minWidth: 10, - alias: 'widget.slidertip', - /** - * @cfg {Array} [offsets=null] - * Offsets for aligning the tip to the slider. See {@link Ext.util.Positionable#alignTo}. Default values - * for offsets are provided by specifying the {@link #position} config. - */ - offsets: null, - /** - * @cfg {String} [align=null] - * Alignment configuration for the tip to the slider. See {@link Ext.util.Positionable#alignTo}. Default - * values for alignment are provided by specifying the {@link #position} config. - */ - align: null, - /** - * @cfg {String} [position=For horizontal sliders, "top", for vertical sliders, "left"] - * Sets the position for where the tip will be displayed related to the thumb. This sets - * defaults for {@link #align} and {@link #offsets} configurations. If {@link #align} or - * {@link #offsets} configurations are specified, they will override the defaults defined - * by position. - */ - position: '', - defaultVerticalPosition: 'left', - defaultHorizontalPosition: 'top', - isSliderTip: true, - init: function(slider) { - var me = this, - align, offsets; - if (!me.position) { - me.position = slider.vertical ? me.defaultVerticalPosition : me.defaultHorizontalPosition; - } - switch (me.position) { - case 'top': - offsets = [ - 0, - -10 - ]; - align = 'b-t?'; - break; - case 'bottom': - offsets = [ - 0, - 10 - ]; - align = 't-b?'; - break; - case 'left': - offsets = [ - -10, - 0 - ]; - align = 'r-l?'; - break; - case 'right': - offsets = [ - 10, - 0 - ]; - align = 'l-r?'; - } - if (!me.align) { - me.align = align; - } - if (!me.offsets) { - me.offsets = offsets; - } - slider.on({ - scope: me, - dragstart: me.onSlide, - drag: me.onSlide, - dragend: me.hide, - destroy: me.destroy - }); - }, - /** - * @private - * Called whenever a dragstart or drag event is received on the associated Thumb. - * Aligns the Tip with the Thumb's new position. - * @param {Ext.slider.MultiSlider} slider The slider - * @param {Ext.event.Event} e The Event object - * @param {Ext.slider.Thumb} thumb The thumb that the Tip is attached to - */ - onSlide: function(slider, e, thumb) { - var me = this; - me.show(); - me.update(me.getText(thumb)); - me.el.alignTo(thumb.el, me.align, me.offsets); - }, - /** - * Used to create the text that appears in the Tip's body. By default this just returns the value of the Slider - * Thumb that the Tip is attached to. Override to customize. - * @param {Ext.slider.Thumb} thumb The Thumb that the Tip is attached to - * @return {String} The text to display in the tip - * @protected - * @template - */ - getText: function(thumb) { - return String(thumb.value); - } -}); - -/** - * Slider which supports vertical or horizontal orientation, keyboard adjustments, configurable snapping, axis clicking - * and animation. Can be added as an item to any container. - * - * Sliders can be created with more than one thumb handle by passing an array of values instead of a single one: - * - * @example - * Ext.create('Ext.slider.Multi', { - * width: 200, - * values: [25, 50, 75], - * increment: 5, - * minValue: 0, - * maxValue: 100, - * - * // this defaults to true, setting to false allows the thumbs to pass each other - * constrainThumbs: false, - * renderTo: Ext.getBody() - * }); - */ -Ext.define('Ext.slider.Multi', { - extend: 'Ext.form.field.Base', - alias: 'widget.multislider', - alternateClassName: 'Ext.slider.MultiSlider', - requires: [ - 'Ext.slider.Thumb', - 'Ext.slider.Tip', - 'Ext.Number', - 'Ext.util.Format', - 'Ext.Template' - ], - /** - * @cfg {Number} value - * A value with which to initialize the slider. Setting this will only result in the creation - * of a single slider thumb; if you want multiple thumbs then use the {@link #values} config instead. - * - * Defaults to #minValue. - */ - /** - * @cfg {Number[]} values - * Array of Number values with which to initalize the slider. A separate slider thumb will be created for each value - * in this array. This will take precedence over the single {@link #value} config. - */ - /** - * @cfg {Boolean} vertical - * Orient the Slider vertically rather than horizontally. - */ - vertical: false, - /** - * @cfg {Number} minValue - * The minimum value for the Slider. - */ - minValue: 0, - /** - * @cfg {Number} maxValue - * The maximum value for the Slider. - */ - maxValue: 100, - /** - * @cfg {Number/Boolean} decimalPrecision The number of decimal places to which to round the Slider's value. - * - * To disable rounding, configure as **false**. - */ - decimalPrecision: 0, - /** - * @cfg {Number} keyIncrement - * How many units to change the Slider when adjusting with keyboard navigation. If the increment - * config is larger, it will be used instead. - */ - keyIncrement: 1, - /** - * @cfg {Number} [pageSize=10] - * How many units to change the Slider when using PageUp and PageDown keys. - */ - pageSize: 10, - /** - * @cfg {Number} increment - * How many units to change the slider when adjusting by drag and drop. Use this option to enable 'snapping'. - */ - increment: 0, - /** - * @cfg {Boolean} [zeroBasedSnapping=false] - * Set to `true` to calculate snap points based on {@link #increment}s from zero as opposed to - * from this Slider's {@link #minValue}. - * - * By Default, valid snap points are calculated starting {@link #increment}s from the {@link #minValue} - */ - /** - * @private - * @property {Number[]} clickRange - * Determines whether or not a click to the slider component is considered to be a user request to change the value. Specified as an array of [top, bottom], - * the click event's 'top' property is compared to these numbers and the click only considered a change request if it falls within them. e.g. if the 'top' - * value of the click event is 4 or 16, the click is not considered a change request as it falls outside of the [5, 15] range - */ - clickRange: [ - 5, - 15 - ], - /** - * @cfg {Boolean} clickToChange - * Determines whether or not clicking on the Slider axis will change the slider. - */ - clickToChange: true, - /** - * @cfg {Object/Boolean} animate - * Turn on or off animation. May be an animation configuration object: - * - * animate: { - * duration: 3000, - * easing: 'easeIn' - * } - */ - animate: true, - /** - * @property {Boolean} dragging - * True while the thumb is in a drag operation - */ - dragging: false, - /** - * @cfg {Boolean} constrainThumbs - * True to disallow thumbs from overlapping one another. - */ - constrainThumbs: true, - /** - * @cfg {Object/Boolean} useTips - * True to use an {@link Ext.slider.Tip} to display tips for the value. This option may also - * provide a configuration object for an {@link Ext.slider.Tip}. - */ - useTips: true, - /** - * @cfg {Function} [tipText=undefined] - * A function used to display custom text for the slider tip. - * - * Defaults to null, which will use the default on the plugin. - * - * @cfg {Ext.slider.Thumb} tipText.thumb The Thumb that the Tip is attached to - * @cfg {String} tipText.return The text to display in the tip - */ - tipText: null, - /** - * @inheritdoc - */ - defaultBindProperty: 'values', - /** - * @event beforechange - * Fires before the slider value is changed. By returning false from an event handler, you can cancel the - * event and prevent the slider from changing. - * @param {Ext.slider.Multi} slider The slider - * @param {Number} newValue The new value which the slider is being changed to. - * @param {Number} oldValue The old value which the slider was previously. - */ - /** - * @event change - * Fires when the slider value is changed. - * @param {Ext.slider.Multi} slider The slider - * @param {Number} newValue The new value which the slider has been changed to. - * @param {Ext.slider.Thumb} thumb The thumb that was changed - */ - /** - * @event changecomplete - * Fires when the slider value is changed by the user and any drag operations have completed. - * @param {Ext.slider.Multi} slider The slider - * @param {Number} newValue The new value which the slider has been changed to. - * @param {Ext.slider.Thumb} thumb The thumb that was changed - */ - /** - * @event dragstart - * Fires after a drag operation has started. - * @param {Ext.slider.Multi} slider The slider - * @param {Ext.event.Event} e The event fired from Ext.dd.DragTracker - */ - /** - * @event drag - * Fires continuously during the drag operation while the mouse is moving. - * @param {Ext.slider.Multi} slider The slider - * @param {Ext.event.Event} e The event fired from Ext.dd.DragTracker - */ - /** - * @event dragend - * Fires after the drag operation has completed. - * @param {Ext.slider.Multi} slider The slider - * @param {Ext.event.Event} e The event fired from Ext.dd.DragTracker - */ - ariaRole: 'slider', - focusable: true, - needArrowKeys: true, - tabIndex: 0, - focusCls: 'slider-focus', - childEls: [ - 'endEl', - 'innerEl' - ], - // note: {id} here is really {inputId}, but {cmpId} is available - fieldSubTpl: [ - '
    tabindex="{tabIdx}"', - ' {$}="{.}"', - '>', - '', - '
    ', - { - renderThumbs: function(out, values) { - var me = values.$comp, - i = 0, - thumbs = me.thumbs, - len = thumbs.length, - thumb, thumbConfig; - for (; i < len; i++) { - thumb = thumbs[i]; - thumbConfig = thumb.getElConfig(); - thumbConfig.id = me.id + '-thumb-' + i; - Ext.DomHelper.generateMarkup(thumbConfig, out); - } - }, - disableFormats: true - } - ], - horizontalProp: 'left', - initValue: function() { - var me = this, - extValueFrom = Ext.valueFrom, - // Fallback for initial values: values config -> value config -> minValue config -> 0 - values = extValueFrom(me.values, [ - extValueFrom(me.value, extValueFrom(me.minValue, 0)) - ]), - i = 0, - len = values.length; - // Store for use in dirty check - me.originalValue = values; - // Add a thumb for each value, enforcing configured constraints - for (; i < len; i++) { - me.addThumb(me.normalizeValue(values[i])); - } - }, - initComponent: function() { - var me = this, - tipPlug, hasTip, p, pLen, plugins; - /** - * @property {Array} thumbs - * Array containing references to each thumb - */ - me.thumbs = []; - me.keyIncrement = Math.max(me.increment, me.keyIncrement); - me.extraFieldBodyCls = Ext.baseCSSPrefix + 'slider-ct-' + (me.vertical ? 'vert' : 'horz'); - me.callParent(); - // only can use it if it exists. - if (me.useTips) { - if (Ext.isObject(me.useTips)) { - tipPlug = Ext.apply({}, me.useTips); - } else { - tipPlug = me.tipText ? { - getText: me.tipText - } : {}; - } - plugins = me.plugins = me.plugins || []; - pLen = plugins.length; - for (p = 0; p < pLen; p++) { - if (plugins[p].isSliderTip) { - hasTip = true; - break; - } - } - if (!hasTip) { - me.plugins.push(new Ext.slider.Tip(tipPlug)); - } - } - }, - /** - * Creates a new thumb and adds it to the slider - * @param {Number} [value=0] The initial value to set on the thumb. - * @return {Ext.slider.Thumb} The thumb - */ - addThumb: function(value) { - var me = this, - thumb = new Ext.slider.Thumb({ - ownerCt: me, - value: value, - slider: me, - index: me.thumbs.length, - constrain: me.constrainThumbs, - disabled: !!me.readOnly - }); - me.thumbs.push(thumb); - //render the thumb now if needed - if (me.rendered) { - thumb.render(); - } - return thumb; - }, - /** - * @private - * Moves the given thumb above all other by increasing its z-index. This is called when as drag - * any thumb, so that the thumb that was just dragged is always at the highest z-index. This is - * required when the thumbs are stacked on top of each other at one of the ends of the slider's - * range, which can result in the user not being able to move any of them. - * @param {Ext.slider.Thumb} topThumb The thumb to move to the top - */ - promoteThumb: function(topThumb) { - var thumbs = this.thumbStack || (this.thumbStack = Ext.Array.slice(this.thumbs)), - ln = thumbs.length, - zIndex = 10000, - i; - // Move topthumb to position zero - if (thumbs[0] !== topThumb) { - Ext.Array.remove(thumbs, topThumb); - thumbs.unshift(topThumb); - } - // Then shuffle the zIndices - for (i = 0; i < ln; i++) { - thumbs[i].el.setStyle('zIndex', zIndex); - zIndex -= 1000; - } - }, - getSubTplData: function(fieldData) { - var me = this, - data, ariaAttr; - data = Ext.apply(me.callParent([ - fieldData - ]), { - $comp: me, - vertical: me.vertical ? Ext.baseCSSPrefix + 'slider-vert' : Ext.baseCSSPrefix + 'slider-horz', - minValue: me.minValue, - maxValue: me.maxValue, - value: me.value, - tabIdx: me.tabIndex, - childElCls: '' - }); - ariaAttr = data.inputElAriaAttributes; - if (ariaAttr) { - ariaAttr['aria-orientation'] = me.vertical ? 'vertical' : 'horizontal'; - ariaAttr['aria-valuemin'] = me.minValue; - ariaAttr['aria-valuemax'] = me.maxValue; - ariaAttr['aria-valuenow'] = me.value; - } - return data; - }, - onRender: function() { - var me = this, - thumbs = me.thumbs, - len = thumbs.length, - i = 0, - thumb; - me.callParent(arguments); - for (i = 0; i < len; i++) { - thumb = thumbs[i]; - thumb.el = me.el.getById(me.id + '-thumb-' + i); - thumb.onRender(); - } - }, - /** - * @private - * Adds keyboard and mouse listeners on this.el. Ignores click events on the internal focus element. - */ - initEvents: function() { - var me = this; - me.callParent(); - me.mon(me.el, { - scope: me, - mousedown: me.onMouseDown, - keydown: me.onKeyDown - }); - }, - onDragStart: Ext.emptyFn, - onDragEnd: Ext.emptyFn, - /** - * @private - * Given an `[x, y]` position within the slider's track (Points outside the slider's track are coerced to either the minimum or maximum value), - * calculate how many pixels **from the slider origin** (left for horizontal Sliders and bottom for vertical Sliders) that point is. - * - * If the point is outside the range of the Slider's track, the return value is `undefined` - * @param {Number[]} xy The point to calculate the track point for - */ - getTrackpoint: function(xy) { - var me = this, - vertical = me.vertical, - sliderTrack = me.innerEl, - trackLength, result, positionProperty; - if (vertical) { - positionProperty = 'top'; - trackLength = sliderTrack.getHeight(); - } else { - positionProperty = me.horizontalProp; - trackLength = sliderTrack.getWidth(); - } - xy = me.transformTrackPoints(sliderTrack.translatePoints(xy)); - result = Ext.Number.constrain(xy[positionProperty], 0, trackLength); - return vertical ? trackLength - result : result; - }, - transformTrackPoints: Ext.identityFn, - // Base field checkChange method will fire 'change' event with signature common to all fields, - // but Slider fires the same event with different signature. Hence we disable checkChange here - // to avoid breakage. - checkChange: Ext.emptyFn, - /** - * @private - * Mousedown handler for the slider. If the clickToChange is enabled and the click was not on the draggable 'thumb', - * this calculates the new value of the slider and tells the implementation (Horizontal or Vertical) to move the thumb - * @param {Ext.event.Event} e The click event - */ - onMouseDown: function(e) { - var me = this, - thumbClicked = false, - i = 0, - thumbs = me.thumbs, - len = thumbs.length, - trackPoint; - if (me.disabled) { - return; - } - //see if the click was on any of the thumbs - for (; !thumbClicked && i < len; i++) { - thumbClicked = thumbClicked || e.target === thumbs[i].el.dom; - } - // Focus ourselves before setting the value. This allows other - // fields that have blur handlers (for example, date/number field) - // to take care of themselves first. This is important for - // databinding. - me.focus(); - if (me.clickToChange && !thumbClicked) { - trackPoint = me.getTrackpoint(e.getXY()); - if (trackPoint !== undefined) { - me.onClickChange(trackPoint); - } - } - }, - /** - * @private - * Moves the thumb to the indicated position. - * Only changes the value if the click was within this.clickRange. - * @param {Number} trackPoint local pixel offset **from the origin** (left for horizontal and bottom for vertical) along the Slider's axis at which the click event occured. - */ - onClickChange: function(trackPoint) { - var me = this, - thumb, index; - // How far along the track *from the origin* was the click. - // If vertical, the origin is the bottom of the slider track. - //find the nearest thumb to the click event - thumb = me.getNearest(trackPoint); - if (!thumb.disabled) { - index = thumb.index; - me.setValue(index, Ext.util.Format.round(me.reversePixelValue(trackPoint), me.decimalPrecision), undefined, true); - } - }, - /** - * @private - * Returns the nearest thumb to a click event, along with its distance - * @param {Number} trackPoint local pixel position along the Slider's axis to find the Thumb for - * @return {Object} The closest thumb object and its distance from the click event - */ - getNearest: function(trackPoint) { - var me = this, - clickValue = me.reversePixelValue(trackPoint), - nearestDistance = me.getRange() + 5, - //add a small fudge for the end of the slider - nearest = null, - thumbs = me.thumbs, - i = 0, - len = thumbs.length, - thumb, value, dist; - for (; i < len; i++) { - thumb = me.thumbs[i]; - value = thumb.value; - dist = Math.abs(value - clickValue); - if (Math.abs(dist) <= nearestDistance) { - // this makes sure that thumbs will stay in order - if (nearest && nearest.value == value && value > clickValue && thumb.index > nearest.index) { - - continue; - } - nearest = thumb; - nearestDistance = dist; - } - } - return nearest; - }, - /** - * @private - * Handler for any keypresses captured by the slider. If the key is UP or RIGHT, the thumb is moved along to the right - * by this.keyIncrement. If DOWN or LEFT it is moved left. Pressing CTRL moves the slider to the end in either direction - * @param {Ext.event.Event} e The Event object - */ - onKeyDown: function(e) { - var me = this, - ariaDom = me.ariaEl.dom, - k, val; - k = e.getKey(); - /* - * The behaviour for keyboard handling with multiple thumbs is currently undefined. - * There's no real sane default for it, so leave it like this until we come up - * with a better way of doing it. - */ - if (me.disabled || me.thumbs.length !== 1) { - // Must not mingle with the Tab key! - if (k !== e.TAB) { - e.preventDefault(); - } - return; - } - switch (k) { - case e.UP: - case e.RIGHT: - val = e.ctrlKey ? me.maxValue : me.getValue(0) + me.keyIncrement; - break; - case e.DOWN: - case e.LEFT: - val = e.ctrlKey ? me.minValue : me.getValue(0) - me.keyIncrement; - break; - case e.HOME: - val = me.minValue; - break; - case e.END: - val = me.maxValue; - break; - case e.PAGE_UP: - val = me.getValue(0) + me.pageSize; - break; - case e.PAGE_DOWN: - val = me.getValue(0) - me.pageSize; - break; - } - if (val !== undefined) { - e.stopEvent(); - val = me.normalizeValue(val); - me.setValue(0, val, undefined, true); - if (ariaDom) { - ariaDom.setAttribute('aria-valuenow', val); - } - } - }, - /** - * @private - * Returns a snapped, constrained value when given a desired value - * @param {Number} value Raw number value - * @return {Number} The raw value rounded to the correct d.p. and constrained within the set max and min values - */ - normalizeValue: function(v) { - var me = this, - snapFn = me.zeroBasedSnapping ? 'snap' : 'snapInRange'; - v = Ext.Number[snapFn](v, me.increment, me.minValue, me.maxValue); - v = Ext.util.Format.round(v, me.decimalPrecision); - v = Ext.Number.constrain(v, me.minValue, me.maxValue); - return v; - }, - /** - * Sets the minimum value for the slider instance. If the current value is less than the minimum value, the current - * value will be changed. - * @param {Number} val The new minimum value - */ - setMinValue: function(val) { - var me = this, - thumbs = me.thumbs, - len = thumbs.length, - ariaDom = me.ariaEl.dom, - thumb, i; - me.minValue = val; - for (i = 0; i < len; ++i) { - thumb = thumbs[i]; - if (thumb.value < val) { - me.setValue(i, val, false); - } - } - if (ariaDom) { - ariaDom.setAttribute('aria-valuemin', val); - } - me.syncThumbs(); - }, - /** - * Sets the maximum value for the slider instance. If the current value is more than the maximum value, the current - * value will be changed. - * @param {Number} val The new maximum value - */ - setMaxValue: function(val) { - var me = this, - thumbs = me.thumbs, - len = thumbs.length, - ariaDom = me.ariaEl.dom, - thumb, i; - me.maxValue = val; - for (i = 0; i < len; ++i) { - thumb = thumbs[i]; - if (thumb.value > val) { - me.setValue(i, val, false); - } - } - if (ariaDom) { - ariaDom.setAttribute('aria-valuemax', val); - } - me.syncThumbs(); - }, - /** - * Programmatically sets the value of the Slider. Ensures that the value is constrained within the minValue and - * maxValue. - * - * Setting the second slider's value without animation: - * - * mySlider.setValue(1, 50, false); - * - * Setting multiple values with animation: - * - * mySlider.setValue([20, 40, 60], true); - * - * @param {Number/Number[]} index Index of the thumb to move. Alternatively, it can be an array of values to set - * for each thumb in the slider. - * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue) - * @param {Object/Boolean} [animate] `false` to not animate. `true` to use the default animation. This may also be an - * animate configuration object, see {@link #cfg-animate}. If this configuration is omitted, the {@link #cfg-animate} configuration - * will be used. - * @return {Ext.slider.Multi} this - */ - setValue: function(index, value, animate, changeComplete) { - var me = this, - thumbs = me.thumbs, - ariaDom = me.ariaEl.dom, - thumb, len, i, values; - if (Ext.isArray(index)) { - values = index; - animate = value; - for (i = 0 , len = values.length; i < len; ++i) { - thumb = thumbs[i]; - if (thumb) { - me.setValue(i, values[i], animate); - } - } - return me; - } - thumb = me.thumbs[index]; - // ensures value is contstrained and snapped - value = me.normalizeValue(value); - if (value !== thumb.value && me.fireEvent('beforechange', me, value, thumb.value, thumb) !== false) { - thumb.value = value; - if (me.rendered) { - if (Ext.isDefined(animate)) { - animate = animate === false ? false : animate; - } else { - animate = me.animate; - } - thumb.move(me.calculateThumbPosition(value), animate); - // At this moment we can only handle one thumb wrt ARIA - if (index === 0 && ariaDom) { - ariaDom.setAttribute('aria-valuenow', value); - } - me.fireEvent('change', me, value, thumb); - me.checkDirty(); - if (changeComplete) { - me.fireEvent('changecomplete', me, value, thumb); - } - } - } - return me; - }, - /** - * @private - * Given a value within this Slider's range, calculates a Thumb's percentage CSS position to map that value. - */ - calculateThumbPosition: function(v) { - var me = this, - minValue = me.minValue, - pos = (v - minValue) / me.getRange() * 100; - if (isNaN(pos)) { - pos = 0; - } - return pos; - }, - /** - * @private - * Returns the ratio of pixels to mapped values. e.g. if the slider is 200px wide and maxValue - minValue is 100, - * the ratio is 2 - * @return {Number} The ratio of pixels to mapped values - */ - getRatio: function() { - var me = this, - innerEl = me.innerEl, - trackLength = me.vertical ? innerEl.getHeight() : innerEl.getWidth(), - valueRange = me.getRange(); - return valueRange === 0 ? trackLength : (trackLength / valueRange); - }, - getRange: function() { - return this.maxValue - this.minValue; - }, - /** - * @private - * Given a pixel location along the slider, returns the mapped slider value for that pixel. - * E.g. if we have a slider 200px wide with minValue = 100 and maxValue = 500, reversePixelValue(50) - * returns 200 - * @param {Number} pos The position along the slider to return a mapped value for - * @return {Number} The mapped value for the given position - */ - reversePixelValue: function(pos) { - return this.minValue + (pos / this.getRatio()); - }, - /** - * @private - * Given a Thumb's percentage position along the slider, returns the mapped slider value for that pixel. - * E.g. if we have a slider 200px wide with minValue = 100 and maxValue = 500, reversePercentageValue(25) - * returns 200 - * @param {Number} pos The percentage along the slider track to return a mapped value for - * @return {Number} The mapped value for the given position - */ - reversePercentageValue: function(pos) { - return this.minValue + this.getRange() * (pos / 100); - }, - onDisable: function() { - var me = this, - i = 0, - thumbs = me.thumbs, - len = thumbs.length, - thumb, el, xy; - me.callParent(); - for (; i < len; i++) { - thumb = thumbs[i]; - el = thumb.el; - thumb.disable(); - if (Ext.isIE) { - //IE breaks when using overflow visible and opacity other than 1. - //Create a place holder for the thumb and display it. - xy = el.getXY(); - el.hide(); - me.innerEl.addCls(me.disabledCls).dom.disabled = true; - if (!me.thumbHolder) { - me.thumbHolder = me.endEl.createChild({ - role: 'presentation', - cls: Ext.baseCSSPrefix + 'slider-thumb ' + me.disabledCls - }); - } - me.thumbHolder.show().setXY(xy); - } - } - }, - onEnable: function() { - var me = this, - i = 0, - thumbs = me.thumbs, - len = thumbs.length, - thumb, el; - this.callParent(); - for (; i < len; i++) { - thumb = thumbs[i]; - el = thumb.el; - thumb.enable(); - if (Ext.isIE) { - me.innerEl.removeCls(me.disabledCls).dom.disabled = false; - if (me.thumbHolder) { - me.thumbHolder.hide(); - } - el.show(); - me.syncThumbs(); - } - } - }, - /** - * Synchronizes thumbs position to the proper proportion of the total component width based on the current slider - * {@link #value}. This will be called automatically when the Slider is resized by a layout, but if it is rendered - * auto width, this method can be called from another resize handler to sync the Slider if necessary. - */ - syncThumbs: function() { - if (this.rendered) { - var thumbs = this.thumbs, - length = thumbs.length, - i = 0; - for (; i < length; i++) { - thumbs[i].move(this.calculateThumbPosition(thumbs[i].value)); - } - } - }, - /** - * Returns the current value of the slider - * @param {Number} index The index of the thumb to return a value for - * @return {Number/Number[]} The current value of the slider at the given index, or an array of all thumb values if - * no index is given. - */ - getValue: function(index) { - return Ext.isNumber(index) ? this.thumbs[index].value : this.getValues(); - }, - /** - * Returns an array of values - one for the location of each thumb - * @return {Number[]} The set of thumb values - */ - getValues: function() { - var values = [], - i = 0, - thumbs = this.thumbs, - len = thumbs.length; - for (; i < len; i++) { - values.push(thumbs[i].value); - } - return values; - }, - getSubmitValue: function() { - var me = this; - return (me.disabled || !me.submitValue) ? null : me.getValue(); - }, - reset: function() { - var me = this, - arr = [].concat(me.originalValue), - a = 0, - aLen = arr.length, - val; - for (; a < aLen; a++) { - val = arr[a]; - me.setValue(a, val); - } - me.clearInvalid(); - // delete here so we reset back to the original state - delete me.wasValid; - }, - setReadOnly: function(readOnly) { - var me = this, - thumbs = me.thumbs, - len = thumbs.length, - i = 0; - me.callParent(arguments); - readOnly = me.readOnly; - for (; i < len; ++i) { - if (readOnly) { - thumbs[i].disable(); - } else { - thumbs[i].enable(); - } - } - }, - beforeDestroy: function() { - var me = this, - thumbs = me.thumbs, - t = 0, - tLen = thumbs.length, - thumb; - if (me.rendered) { - for (; t < tLen; t++) { - thumb = thumbs[t]; - Ext.destroy(thumb); - } - } - me.callParent(); - } -}); - -/** - * Slider which supports vertical or horizontal orientation, keyboard adjustments, configurable snapping, axis clicking - * and animation. Can be added as an item to any container. - * - * @example - * Ext.create('Ext.slider.Single', { - * width: 200, - * value: 50, - * increment: 10, - * minValue: 0, - * maxValue: 100, - * renderTo: Ext.getBody() - * }); - * - * The class Ext.slider.Single is aliased to Ext.Slider for backwards compatibility. - */ -Ext.define('Ext.slider.Single', { - extend: 'Ext.slider.Multi', - alias: [ - 'widget.slider', - 'widget.sliderfield' - ], - alternateClassName: [ - 'Ext.Slider', - 'Ext.form.SliderField', - 'Ext.slider.SingleSlider', - 'Ext.slider.Slider' - ], - /** - * @inheritdoc - */ - defaultBindProperty: 'value', - initComponent: function() { - if (this.publishOnComplete) { - this.valuePublishEvent = 'changecomplete'; - } - this.callParent(); - }, - /** - * @cfg {Boolean} [publishOnComplete=true] - * This controls when the value of the slider is published to the `ViewModel`. By - * default this is done only when the thumb is released (the change is complete). To - * cause this to happen on every change of the thumb position, specify `false`. This - * setting is `true` by default for improved performance on slower devices (such as - * older browsers or tablets). - */ - publishOnComplete: true, - /** - * Returns the current value of the slider - * @return {Number} The current value of the slider - */ - getValue: function() { - // just returns the value of the first thumb, which should be the only one in a single slider - return this.callParent([ - 0 - ]); - }, - /** - * Programmatically sets the value of the Slider. Ensures that the value is constrained within the minValue and - * maxValue. - * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue) - * @param {Object/Boolean} [animate] `false` to not animate. `true` to use the default animation. This may also be an - * animate configuration object, see {@link #cfg-animate}. If this configuration is omitted, the {@link #cfg-animate} configuration - * will be used. - */ - setValue: function(value, animate) { - var args = arguments, - len = args.length; - // this is to maintain backwards compatibility for sliders with only one thumb. Usually you must pass the thumb - // index to setValue, but if we only have one thumb we inject the index here first if given the multi-slider - // signature without the required index. The index will always be 0 for a single slider - if (len === 1 || (len <= 3 && typeof args[1] !== 'number')) { - args = Ext.toArray(args); - args.unshift(0); - } - return this.callParent(args); - }, - /** - * @private - */ - getNearest: function() { - // Since there's only 1 thumb, it's always the nearest - return this.thumbs[0]; - } -}); - -/** - * A Widget-based implementation of a slider. - * @since 5.0.0 - */ -Ext.define('Ext.slider.Widget', { - extend: 'Ext.Widget', - alias: 'widget.sliderwidget', - // Required to pull in the styles - requires: [ - 'Ext.slider.Multi' - ], - cachedConfig: { - /** - * @cfg {Boolean} vertical - * Orients the slider vertically rather than horizontally. - */ - vertical: false - }, - config: { - /** - * @cfg {Boolean} clickToChange - * Determines whether or not clicking on the Slider axis will change the slider. - */ - clickToChange: true, - ui: 'widget', - /** - * @cfg {Number/Number[]} value - * One more values for the position of the slider's thumb(s). - */ - value: 0, - /** - * @cfg {Number} minValue - * The minimum value for any slider thumb. - */ - minValue: 0, - /** - * @cfg {Number} maxValue - * The maximum value for any slider thumb. - */ - maxValue: 100, - /** - * @cfg {Boolean} [publishOnComplete=true] - * This controls when the value of the slider is published to the `ViewModel`. By - * default this is done only when the thumb is released (the change is complete). To - * cause this to happen on every change of the thumb position, specify `false`. This - * setting is `true` by default for improved performance on slower devices (such as - * older browsers or tablets). - */ - publishOnComplete: true, - /** - * @cfg {Object} twoWayBindable - * This object is a map of config property names holding a `true` if changes to - * that config should written back to its binding. Most commonly this is used to - * indicate that the `value` config should be monitored and changes written back - * to the bound value. - */ - twoWayBindable: { - value: 1 - } - }, - decimalPrecision: 0, - defaultBindProperty: 'value', - element: { - reference: 'element', - cls: Ext.baseCSSPrefix + 'slider', - listeners: { - mousedown: 'onMouseDown', - dragstart: 'cancelDrag', - drag: 'cancelDrag', - dragend: 'cancelDrag' - }, - children: [ - { - reference: 'endEl', - cls: Ext.baseCSSPrefix + 'slider-end', - children: [ - { - reference: 'innerEl', - cls: Ext.baseCSSPrefix + 'slider-inner' - } - ] - } - ] - }, - thumbCls: Ext.baseCSSPrefix + 'slider-thumb', - horizontalProp: 'left', - // This property is set to false onMouseDown and deleted onMouseUp. It is used only - // by applyValue when it passes the animate parameter to setThumbValue. - animateOnSetValue: undefined, - applyValue: function(value) { - var me = this, - animate = me.animateOnSetValue, - i, len; - if (Ext.isArray(value)) { - value = Ext.Array.from(value); - for (i = 0 , len = value.length; i < len; ++i) { - me.setThumbValue(i, value[i] = me.normalizeValue(value[i]), animate, true); - } - } else { - value = me.normalizeValue(value); - me.setThumbValue(0, value, animate, true); - } - return value; - }, - updateVertical: function(vertical, oldVertical) { - this.element.removeCls(Ext.baseCSSPrefix + 'slider-' + (oldVertical ? 'vert' : 'horz')); - this.element.addCls(Ext.baseCSSPrefix + 'slider-' + (vertical ? 'vert' : 'horz')); - }, - updateHeight: function(height, oldHeight) { - this.callParent([ - height, - oldHeight - ]); - this.endEl.dom.style.height = this.innerEl.dom.style.height = '100%'; - }, - cancelDrag: function(e) { - // prevent the touch scroller from scrolling when the slider is being dragged - e.stopPropagation(); - }, - getThumb: function(ordinal) { - var me = this, - thumbConfig, - result = (me.thumbs || (me.thumbs = []))[ordinal]; - if (!result) { - thumbConfig = { - cls: me.thumbCls, - style: {} - }; - thumbConfig['data-thumbIndex'] = ordinal; - result = me.thumbs[ordinal] = me.innerEl.createChild(thumbConfig); - } - return result; - }, - getThumbPositionStyle: function() { - return this.getVertical() ? 'bottom' : (this.rtl && Ext.rtl ? 'right' : 'left'); - }, - // // TODO: RTL - // getRenderTree: function() { - // var me = this, - // rtl = me.rtl; - // - // if (rtl && Ext.rtl) { - // me.baseCls += ' ' + (Ext.rtl.util.Renderable.prototype._rtlCls); - // me.horizontalProp = 'right'; - // } else if (rtl === false) { - // me.addCls(Ext.rtl.util.Renderable.prototype._ltrCls); - // } - // - // return me.callParent(); - // }, - update: function() { - var me = this, - values = me.getValue(), - len = values.length, - i; - for (i = 0; i < len; i++) { - this.thumbs[i].dom.style[me.getThumbPositionStyle()] = me.calculateThumbPosition(values[i]) + '%'; - } - }, - onMouseDown: function(e) { - var me = this, - thumb, - trackPoint = e.getXY(), - delta; - if (!me.disabled && e.button === 0) { - // Stop any selection caused by mousedown + mousemove - Ext.getDoc().on({ - scope: me, - capture: true, - selectstart: me.stopSelect - }); - thumb = e.getTarget('.' + me.thumbCls, null, true); - if (thumb) { - me.animateOnSetValue = false; - me.promoteThumb(thumb); - me.captureMouse(me.onMouseMove, me.onMouseUp, [ - thumb - ], 1); - delta = me.pointerOffset = thumb.getXY(); - // Work out the delta of the pointer from the dead centre of the thumb. - // Slider.getTrackPoint positions the centre of the slider at the reported - // pointer position, so we have to correct for that in getValueFromTracker. - delta[0] += Math.floor(thumb.getWidth() / 2) - trackPoint[0]; - delta[1] += Math.floor(thumb.getHeight() / 2) - trackPoint[1]; - } else { - if (me.getClickToChange()) { - trackPoint = me.getTrackpoint(trackPoint); - if (trackPoint != null) { - me.onClickChange(trackPoint); - } - } - } - } - }, - /** - * @private - * Moves the thumb to the indicated position. - * Only changes the value if the click was within this.clickRange. - * @param {Number} trackPoint local pixel offset **from the origin** (left for horizontal and bottom for vertical) along the Slider's axis at which the click event occured. - */ - onClickChange: function(trackPoint) { - var me = this, - thumb, index, value; - // How far along the track *from the origin* was the click. - // If vertical, the origin is the bottom of the slider track. - //find the nearest thumb to the click event - thumb = me.getNearest(trackPoint); - index = parseInt(thumb.getAttribute('data-thumbIndex'), 10); - value = Ext.util.Format.round(me.reversePixelValue(trackPoint), me.decimalPrecision); - if (index) { - me.setThumbValue(index, value, undefined, true); - } else { - me.setValue(value); - } - }, - /** - * @private - * Returns the nearest thumb to a click event, along with its distance - * @param {Number} trackPoint local pixel position along the Slider's axis to find the Thumb for - * @return {Object} The closest thumb object and its distance from the click event - */ - getNearest: function(trackPoint) { - var me = this, - clickValue = me.reversePixelValue(trackPoint), - nearestDistance = me.getRange() + 5, - //add a small fudge for the end of the slider - nearest = null, - thumbs = me.thumbs, - i = 0, - len = thumbs.length, - thumb, value, dist; - for (; i < len; i++) { - thumb = thumbs[i]; - value = me.reversePercentageValue(parseInt(thumb.dom.style[me.getThumbPositionStyle()], 10)); - dist = Math.abs(value - clickValue); - if (Math.abs(dist) <= nearestDistance) { - nearest = thumb; - nearestDistance = dist; - } - } - return nearest; - }, - /** - * @private - * Moves the given thumb above all other by increasing its z-index. This is called when as drag - * any thumb, so that the thumb that was just dragged is always at the highest z-index. This is - * required when the thumbs are stacked on top of each other at one of the ends of the slider's - * range, which can result in the user not being able to move any of them. - * @param {Ext.slider.Thumb} topThumb The thumb to move to the top - */ - promoteThumb: function(topThumb) { - var thumbs = this.thumbStack || (this.thumbStack = Ext.Array.slice(this.thumbs)), - ln = thumbs.length, - zIndex = 10000, - i; - // Move topthumb to position zero - if (thumbs[0] !== topThumb) { - Ext.Array.remove(thumbs, topThumb); - thumbs.unshift(topThumb); - } - // Then shuffle the zIndices - for (i = 0; i < ln; i++) { - thumbs[i].el.setStyle('zIndex', zIndex); - zIndex -= 1000; - } - }, - doMouseMove: function(e, thumb, changeComplete) { - var me = this, - trackerXY = e.getXY(), - newValue, thumbIndex, trackPoint; - trackerXY[0] += me.pointerOffset[0]; - trackerXY[1] += me.pointerOffset[1]; - trackPoint = me.getTrackpoint(trackerXY); - // If dragged out of range, value will be undefined - if (trackPoint) { - newValue = me.reversePixelValue(trackPoint); - thumbIndex = parseInt(thumb.getAttribute('data-thumbIndex'), 10); - if (thumbIndex || (!changeComplete && me.getPublishOnComplete())) { - me.setThumbValue(thumbIndex, newValue, false, changeComplete); - } else { - me.setValue(newValue); - } - } - }, - onMouseMove: function(e, thumb) { - this.doMouseMove(e, thumb, false); - }, - onMouseUp: function(e, thumb) { - var me = this; - me.doMouseMove(e, thumb, true); - Ext.getDoc().un({ - scope: me, - capture: true, - selectstart: me.stopSelect - }); - delete me.animateOnSetValue; - }, - // expose "undefined" on prototype - stopSelect: function(e) { - e.stopEvent(); - return false; - }, - /** - * Programmatically sets the value of the Slider. Ensures that the value is constrained within the minValue and - * maxValue. - * - * Setting a single value: - * // Set the second slider value, don't animate - * mySlider.setThumbValue(1, 50, false); - * - * Setting multiple values at once - * // Set 3 thumb values, animate - * mySlider.setThumbValue([20, 40, 60], true); - * - * @param {Number/Number[]} index Index of the thumb to move. Alternatively, it can be an array of values to set - * for each thumb in the slider. - * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue) - * @param {Boolean} [animate=true] Turn on or off animation - * @return {Ext.slider.Multi} this - */ - setThumbValue: function(index, value, animate, changeComplete) { - var me = this, - thumb, thumbValue, len, i, values; - if (Ext.isArray(index)) { - values = index; - animate = value; - for (i = 0 , len = values.length; i < len; ++i) { - me.setThumbValue(i, values[i], animate, changeComplete); - } - return me; - } - thumb = me.getThumb(index); - thumbValue = me.reversePercentageValue(parseInt(thumb.dom.style[me.getThumbPositionStyle()], 10)); - // ensures value is contstrained and snapped - value = me.normalizeValue(value); - if (value !== thumbValue && me.fireEvent('beforechange', me, value, thumbValue, thumb) !== false) { - if (me.element.dom) { - // TODO this only handles a single value; need a solution for exposing multiple values to aria. - // Perhaps this should go on each thumb element rather than the outer element. - me.element.set({ - 'aria-valuenow': value, - 'aria-valuetext': value - }); - me.moveThumb(thumb, me.calculateThumbPosition(value), Ext.isDefined(animate) ? animate !== false : me.animate); - me.fireEvent('change', me, value, thumb); - } - } - return me; - }, - /** - * Returns the current value of the slider - * @param {Number} index The index of the thumb to return a value for - * @return {Number/Number[]} The current value of the slider at the given index, or an array of all thumb values if - * no index is given. - */ - getValue: function(index) { - var me = this, - value; - if (Ext.isNumber(index)) { - value = me.thumbs[index].dom.style[me.getThumbPositionStyle()]; - value = me.reversePercentageValue(parseInt(value, 10)); - } else { - value = me.getValues(); - if (value.length === 1) { - value = value[0]; - } - } - return value; - }, - /** - * Returns an array of values - one for the location of each thumb - * @return {Number[]} The set of thumb values - */ - getValues: function() { - var me = this, - values = [], - i = 0, - thumbs = me.thumbs, - len = thumbs.length; - for (; i < len; i++) { - values.push(me.reversePercentageValue(parseInt(me.thumbs[i].dom.style[me.getThumbPositionStyle()], 10))); - } - return values; - }, - /** - * @private - * move the thumb - */ - moveThumb: function(thumb, v, animate) { - var me = this, - styleProp = me.getThumbPositionStyle(), - to, from; - v += '%'; - if (!animate) { - thumb.dom.style[styleProp] = v; - } else { - to = {}; - to[styleProp] = v; - if (!Ext.supports.GetPositionPercentage) { - from = {}; - from[styleProp] = thumb.dom.style[styleProp]; - } - new Ext.fx.Anim({ - // jshint ignore:line - target: thumb, - duration: 350, - from: from, - to: to - }); - } - }, - /** - * @private - * Returns a snapped, constrained value when given a desired value - * @param {Number} value Raw number value - * @return {Number} The raw value rounded to the correct d.p. and constrained within the set max and min values - */ - normalizeValue: function(v) { - var me = this, - snapFn = me.zeroBasedSnapping ? 'snap' : 'snapInRange'; - v = Ext.Number[snapFn](v, me.increment, me.minValue, me.maxValue); - v = Ext.util.Format.round(v, me.decimalPrecision); - v = Ext.Number.constrain(v, me.minValue, me.maxValue); - return v; - }, - /** - * @private - * Given an `[x, y]` position within the slider's track (Points outside the slider's track are coerced to either the minimum or maximum value), - * calculate how many pixels **from the slider origin** (left for horizontal Sliders and bottom for vertical Sliders) that point is. - * - * If the point is outside the range of the Slider's track, the return value is `undefined` - * @param {Number[]} xy The point to calculate the track point for - */ - getTrackpoint: function(xy) { - var me = this, - vertical = me.getVertical(), - sliderTrack = me.innerEl, - trackLength, result, positionProperty; - if (vertical) { - positionProperty = 'top'; - trackLength = sliderTrack.getHeight(); - } else { - positionProperty = 'left'; - trackLength = sliderTrack.getWidth(); - } - xy = me.transformTrackPoints(sliderTrack.translatePoints(xy)); - result = Ext.Number.constrain(xy[positionProperty], 0, trackLength); - return vertical ? trackLength - result : result; - }, - transformTrackPoints: Ext.identityFn, - /** - * @private - * Given a value within this Slider's range, calculates a Thumb's percentage CSS position to map that value. - */ - calculateThumbPosition: function(v) { - var me = this, - pos = (v - me.getMinValue()) / me.getRange() * 100; - if (isNaN(pos)) { - pos = 0; - } - return pos; - }, - /** - * @private - * Returns the ratio of pixels to mapped values. e.g. if the slider is 200px wide and maxValue - minValue is 100, - * the ratio is 2 - * @return {Number} The ratio of pixels to mapped values - */ - getRatio: function() { - var me = this, - innerEl = me.innerEl, - trackLength = me.getVertical() ? innerEl.getHeight() : innerEl.getWidth(), - valueRange = me.getRange(); - return valueRange === 0 ? trackLength : (trackLength / valueRange); - }, - getRange: function() { - return this.getMaxValue() - this.getMinValue(); - }, - /** - * @private - * Given a pixel location along the slider, returns the mapped slider value for that pixel. - * E.g. if we have a slider 200px wide with minValue = 100 and maxValue = 500, reversePixelValue(50) - * returns 200 - * @param {Number} pos The position along the slider to return a mapped value for - * @return {Number} The mapped value for the given position - */ - reversePixelValue: function(pos) { - return this.getMinValue() + (pos / this.getRatio()); - }, - /** - * @private - * Given a Thumb's percentage position along the slider, returns the mapped slider value for that pixel. - * E.g. if we have a slider 200px wide with minValue = 100 and maxValue = 500, reversePercentageValue(25) - * returns 200 - * @param {Number} pos The percentage along the slider track to return a mapped value for - * @return {Number} The mapped value for the given position - */ - reversePercentageValue: function(pos) { - return this.getMinValue() + this.getRange() * (pos / 100); - }, - captureMouse: function(onMouseMove, onMouseUp, args, appendArgs) { - var me = this, - onMouseupWrap, listeners; - onMouseMove = onMouseMove && Ext.Function.bind(onMouseMove, me, args, appendArgs); - onMouseUp = onMouseUp && Ext.Function.bind(onMouseUp, me, args, appendArgs); - onMouseupWrap = function() { - Ext.getDoc().un(listeners); - if (onMouseUp) { - onMouseUp.apply(me, arguments); - } - }; - listeners = { - mousemove: onMouseMove, - mouseup: onMouseupWrap - }; - // Funnel mousemove events and the final mouseup event back into the gadget - Ext.getDoc().on(listeners); - } -}); - -/** - * @class Ext.sparkline.Shape - * @private - */ -Ext.define('Ext.sparkline.Shape', { - constructor: function(target, id, type, args) { - this.target = target; - this.id = id; - this.type = type; - this.args = args; - }, - append: function() { - this.target.appendShape(this); - return this; - } -}); - -/** - * @class Ext.sparkline.CanvasBase - * @private - */ -Ext.define('Ext.sparkline.CanvasBase', { - requires: [ - 'Ext.sparkline.Shape' - ], - shapeCount: 0, - _pxregex: /(\d+)(px)?\s*$/i, - constructor: function(ownerSparkLine) { - this.owner = ownerSparkLine; - }, - setWidth: function(width) { - this.pixelWidth = width; - }, - setHeight: function(height) { - this.pixelHeight = height; - }, - drawLine: function(x1, y1, x2, y2, lineColor, lineWidth) { - return this.drawShape([ - [ - x1, - y1 - ], - [ - x2, - y2 - ] - ], lineColor, lineWidth); - }, - drawShape: function(path, lineColor, fillColor, lineWidth) { - return this._genShape('Shape', [ - path, - lineColor, - fillColor, - lineWidth - ]); - }, - drawCircle: function(x, y, radius, lineColor, fillColor, lineWidth) { - return this._genShape('Circle', [ - x, - y, - radius, - lineColor, - fillColor, - lineWidth - ]); - }, - drawPieSlice: function(x, y, radius, startAngle, endAngle, lineColor, fillColor) { - return this._genShape('PieSlice', [ - x, - y, - radius, - startAngle, - endAngle, - lineColor, - fillColor - ]); - }, - drawRect: function(x, y, width, height, lineColor, fillColor) { - return this._genShape('Rect', [ - x, - y, - width, - height, - lineColor, - fillColor - ]); - }, - getElement: function() { - return this.el; - }, - /* - * Return the most recently inserted shape id - */ - getLastShapeId: function() { - return this.lastShapeId; - }, - /* - * Clear and reset the canvas - */ - reset: function() { - Ext.raise('reset not implemented'); - }, - /* - * Generate a shape object and id for later rendering - */ - _genShape: function(shapetype, shapeargs) { - var id = this.shapeCount++; - shapeargs.unshift(id); - return new Ext.sparkline.Shape(this, id, shapetype, shapeargs); - }, - /* - * Add a shape to the end of the render queue - */ - appendShape: function(shape) { - Ext.raise('appendShape not implemented'); - }, - /* - * Replace one shape with another - */ - replaceWithShape: function(shapeid, shape) { - Ext.raise('replaceWithShape not implemented'); - }, - /* - * Insert one shape after another in the render queue - */ - insertAfterShape: function(shapeid, shape) { - Ext.raise('insertAfterShape not implemented'); - }, - /* - * Remove a shape from the queue - */ - removeShapeId: function(shapeid) { - Ext.raise('removeShapeId not implemented'); - }, - /* - * Find a shape at the specified x/y co-ordinates - */ - getShapeAt: function(x, y) { - Ext.raise('getShapeAt not implemented'); - }, - /* - * Render all queued shapes onto the canvas - */ - render: function() { - Ext.raise('render not implemented'); - } -}); - -/** - * @private - */ -Ext.define('Ext.sparkline.CanvasCanvas', { - extend: 'Ext.sparkline.CanvasBase', - statics: { - contextOverrides: (function() { - var ratio = window.devicePixelRatio || 1; - return { - moveTo: function(x, y) { - this.$moveTo(x * ratio, y * ratio); - }, - lineTo: function(x, y) { - this.$lineTo(x * ratio, y * ratio); - }, - arc: function(x, y, radius, startAngle, endAngle, counterclockwise) { - this.$arc(x * ratio, y * ratio, radius * ratio, startAngle, endAngle, counterclockwise); - }, - clearRect: function(x, y, width, height) { - this.$clearRect(x * ratio, y * ratio, width * ratio, height * ratio); - } - }; - })() - }, - setWidth: function(width) { - this.callParent(arguments); - this.owner.element.dom.width = width * (window.devicePixelRatio || 1); - }, - setHeight: function(height) { - this.callParent(arguments); - this.owner.element.dom.height = height * (window.devicePixelRatio || 1); - }, - onOwnerUpdate: function() { - var me = this; - me.el = me.owner.element; - me.interact = !me.owner.initialConfig.disableInteraction; - me.shapes = {}; - me.shapeseq = []; - me.currentTargetShapeId = me.lastShapeId = null; - }, - _getContext: function(lineColor, fillColor, lineWidth) { - var context = this.el.dom.getContext('2d'), - overrides = Ext.sparkline.CanvasCanvas.contextOverrides, - name; - if (!this.context) { - for (name in overrides) { - context['$' + name] = context[name]; - } - Ext.apply(context, overrides); - this.context = context; - } - if (lineColor != null) { - context.strokeStyle = lineColor; - } - context.lineWidth = lineWidth || 1; - if (fillColor != null) { - context.fillStyle = fillColor; - } - return context; - }, - reset: function() { - var context = this._getContext(); - context.clearRect(0, 0, this.pixelWidth, this.pixelHeight); - this.shapes = {}; - this.shapeseq = []; - this.currentTargetShapeId = this.lastShapeId = null; - }, - _drawShape: function(shapeid, path, lineColor, fillColor, lineWidth) { - var context = this._getContext(lineColor, fillColor, lineWidth), - i, plen; - context.beginPath(); - context.moveTo(path[0][0] + 0.5, path[0][1] + 0.5); - for (i = 1 , plen = path.length; i < plen; i++) { - context.lineTo(path[i][0] + 0.5, path[i][1] + 0.5); - } - // the 0.5 offset gives us crisp pixel-width lines - if (lineColor != null) { - context.stroke(); - } - if (fillColor != null) { - context.fill(); - } - if (this.targetX != null && this.targetY != null && context.isPointInPath(this.targetX, this.targetY)) { - this.currentTargetShapeId = shapeid; - } - }, - _drawCircle: function(shapeid, x, y, radius, lineColor, fillColor, lineWidth) { - var context = this._getContext(lineColor, fillColor, lineWidth); - context.beginPath(); - context.arc(x, y, radius, 0, 2 * Math.PI, false); - if (this.targetX != null && this.targetY != null && context.isPointInPath(this.targetX, this.targetY)) { - this.currentTargetShapeId = shapeid; - } - if (lineColor !== undefined) { - context.stroke(); - } - if (fillColor !== undefined) { - context.fill(); - } - }, - _drawPieSlice: function(shapeid, x, y, radius, startAngle, endAngle, lineColor, fillColor) { - var context = this._getContext(lineColor, fillColor); - context.beginPath(); - context.moveTo(x, y); - context.arc(x, y, radius, startAngle, endAngle, false); - context.lineTo(x, y); - context.closePath(); - if (lineColor != null) { - context.stroke(); - } - if (fillColor) { - context.fill(); - } - if (this.targetX !== undefined && this.targetY !== undefined && context.isPointInPath(this.targetX, this.targetY)) { - this.currentTargetShapeId = shapeid; - } - }, - _drawRect: function(shapeid, x, y, width, height, lineColor, fillColor) { - return this._drawShape(shapeid, [ - [ - x, - y - ], - [ - x + width, - y - ], - [ - x + width, - y + height - ], - [ - x, - y + height - ], - [ - x, - y - ] - ], lineColor, fillColor); - }, - appendShape: function(shape) { - this.shapes[shape.id] = shape; - this.shapeseq.push(shape.id); - this.lastShapeId = shape.id; - return shape.id; - }, - replaceWithShape: function(shapeid, shape) { - var shapeseq = this.shapeseq, - i; - this.shapes[shape.id] = shape; - for (i = shapeseq.length; i--; ) { - if (shapeseq[i] == shapeid) { - shapeseq[i] = shape.id; - } - } - delete this.shapes[shapeid]; - }, - replaceWithShapes: function(shapeids, shapes) { - var shapeseq = this.shapeseq, - shapemap = {}, - sid, i, first; - for (i = shapeids.length; i--; ) { - shapemap[shapeids[i]] = true; - } - for (i = shapeseq.length; i--; ) { - sid = shapeseq[i]; - if (shapemap[sid]) { - shapeseq.splice(i, 1); - delete this.shapes[sid]; - first = i; - } - } - for (i = shapes.length; i--; ) { - shapeseq.splice(first, 0, shapes[i].id); - this.shapes[shapes[i].id] = shapes[i]; - } - }, - insertAfterShape: function(shapeid, shape) { - var shapeseq = this.shapeseq, - i; - for (i = shapeseq.length; i--; ) { - if (shapeseq[i] === shapeid) { - shapeseq.splice(i + 1, 0, shape.id); - this.shapes[shape.id] = shape; - return; - } - } - }, - removeShapeId: function(shapeid) { - var shapeseq = this.shapeseq, - i; - for (i = shapeseq.length; i--; ) { - if (shapeseq[i] === shapeid) { - shapeseq.splice(i, 1); - break; - } - } - delete this.shapes[shapeid]; - }, - getShapeAt: function(x, y) { - this.targetX = x; - this.targetY = y; - this.render(); - return this.currentTargetShapeId; - }, - render: function() { - var shapeseq = this.shapeseq, - shapes = this.shapes, - shapeCount = shapeseq.length, - context = this._getContext(), - shapeid, shape, i; - context.clearRect(0, 0, this.pixelWidth, this.pixelHeight); - for (i = 0; i < shapeCount; i++) { - shapeid = shapeseq[i]; - shape = shapes[shapeid]; - this['_draw' + shape.type].apply(this, shape.args); - } - if (!this.interact) { - // not interactive so no need to keep the shapes array - this.shapes = {}; - this.shapeseq = []; - } - } -}); - -/** - * @private - */ -Ext.define('Ext.sparkline.VmlCanvas', { - extend: 'Ext.sparkline.CanvasBase', - setWidth: function(width) { - var me = this; - me.callParent(arguments); - me.owner.groupEl.dom.coordsize = me.width + ' ' + (me.height || 0); - me.owner.groupEl.dom.style.width = width + 'px'; - }, - setHeight: function(height) { - var me = this; - me.callParent(arguments); - me.owner.groupEl.dom.coordsize = (me.width || 0) + ' ' + me.height; - me.owner.groupEl.dom.style.height = height + 'px'; - }, - onOwnerUpdate: function() { - var me = this; - me.group = me.owner.groupEl; - me.el = me.owner.element; - me.prerender = []; - }, - _drawShape: function(shapeid, path, lineColor, fillColor, lineWidth) { - var vpath = [], - initial, stroke, fill, closed, plen, i; - for (i = 0 , plen = path.length; i < plen; i++) { - vpath[i] = (path[i][0]) + ',' + (path[i][1]); - } - initial = vpath.splice(0, 1); - lineWidth = lineWidth == null ? 1 : lineWidth; - stroke = lineColor == null ? ' stroked="false" ' : ' strokeWeight="' + lineWidth + 'px" strokeColor="' + lineColor + '" '; - fill = fillColor == null ? ' filled="false"' : ' fillColor="' + fillColor + '" filled="true" '; - closed = vpath[0] === vpath[vpath.length - 1] ? 'x ' : ''; - return [ - '' - ].join(''); - }, - _drawCircle: function(shapeid, x, y, radius, lineColor, fillColor, lineWidth) { - var circumference = radius * 2, - stroke, fill; - x -= radius; - y -= radius; - stroke = lineColor == null ? ' stroked="false" ' : ' strokeWeight="' + lineWidth + 'px" strokeColor="' + lineColor + '" '; - fill = fillColor == null ? ' filled="false"' : ' fillColor="' + fillColor + '" filled="true" '; - return [ - '' - ].join(''); - }, - _drawPieSlice: function(shapeid, x, y, radius, startAngle, endAngle, lineColor, fillColor) { - var vpath, - width = this.pixelWidth, - height = this.pixelHeight, - startx, starty, endx, endy, - stroke = lineColor == null ? ' stroked="false" ' : ' strokeWeight="1px" strokeColor="' + lineColor + '" ', - fill = fillColor == null ? ' filled="false"' : ' fillColor="' + fillColor + '" filled="true" '; - // VML cannot handle start & end angle the same. - if (startAngle === endAngle) { - return ''; - } - if ((endAngle - startAngle) === (2 * Math.PI)) { - startAngle = 0; - // VML seems to have a problem when drawing a full circle that doesn't start 0 - endAngle = (2 * Math.PI); - } - startx = x + Math.round(Math.cos(startAngle) * radius); - starty = y + Math.round(Math.sin(startAngle) * radius); - endx = x + Math.round(Math.cos(endAngle) * radius); - endy = y + Math.round(Math.sin(endAngle) * radius); - if (startx === endx && starty === endy) { - if ((endAngle - startAngle) < Math.PI) { - // Prevent very small slices from being mistaken as a whole pie - return ''; - } - // essentially going to be the entire circle, so ignore startAngle - startx = endx = x + radius; - starty = endy = y; - } - if (startx === endx && starty === endy && (endAngle - startAngle) < Math.PI) { - return ''; - } - vpath = [ - x - radius, - y - radius, - x + radius, - y + radius, - startx, - starty, - endx, - endy - ]; - return [ - '' - ].join(''); - }, - _drawRect: function(shapeid, x, y, width, height, lineColor, fillColor) { - return this._drawShape(shapeid, [ - [ - x, - y - ], - [ - x, - y + height - ], - [ - x + width, - y + height - ], - [ - x + width, - y - ], - [ - x, - y - ] - ], lineColor, fillColor); - }, - reset: function() { - Ext.fly(this.group).empty(); - }, - appendShape: function(shape) { - this.prerender.push(this['_draw' + shape.type].apply(this, shape.args)); - this.lastShapeId = shape.id; - return shape.id; - }, - replaceWithShape: function(shapeid, shape) { - var existing = this.el.getById('jqsshape' + shapeid, true), - vel = this['_draw' + shape.type].apply(this, shape.args); - existing.outerHTML = vel; - }, - replaceWithShapes: function(shapeids, shapes) { - // replace the first shapeid with all the new shapes then toast the remaining old shapes - var existing = this.el.getById('jqsshape' + shapeids[0], true), - replace = '', - slen = shapes.length, - i; - for (i = 0; i < slen; i++) { - replace += this['_draw' + shapes[i].type].apply(this, shapes[i].args); - } - existing.outerHTML = replace; - for (i = 1; i < shapeids.length; i++) { - this.el.getById('jqsshape' + shapeids[i]).destroy(); - } - }, - insertAfterShape: function(shapeid, shape) { - var existing = this.el.getById('jqsshape' + shapeid, true), - vel = this['_draw' + shape.type].apply(this, shape.args); - existing.insertAdjacentHTML('afterEnd', vel); - }, - removeShapeId: function(shapeid) { - var existing = this.el.getById('jqsshape' + shapeid, true); - this.group.removeChild(existing); - }, - getShapeAt: function(x, y) { - var shapeid = this.el.id.substr(8); - return shapeid; - }, - render: function() { - this.group.dom.innerHTML = this.prerender.join(''); - } -}, function() { - Ext.onInternalReady(function() { - var doc = document; - if (doc.namespaces && !doc.namespaces.svml) { - doc.namespaces.add("svml", "urn:schemas-microsoft-com:vml", '#default#VML'); - } - }); -}); - -/** - * @class Ext.sparkline.Base - * - * The base class for ExtJS SparkLines. SparkLines are small, inline graphs used to visually - * display small amounts of data. For large datasets, use the {@link Ext.chart.AbstractChart chart package}. - * - * The SparkLine subclasses accept an {@link #values array of values}, and present the data in different visualizations. - * - * @example - * new Ext.Panel({ - * height: 300, - * width: 600, - * frame: true, - * title: 'Test Sparklines', - * renderTo:document.body, - * bodyPadding: 10, - * - * // Named listeners will resolve to methods in this Panel - * defaultListenerScope: true, - * - * // Named references will be collected, and can be access from this Panel - * referenceHolder: true, - * - * items: [{ - * reference: 'values', - * xtype: 'textfield', - * fieldLabel: 'Values', - * validator: function(v) { - * var result = []; - * - * v = v.replace(/\s/g, ''); - * v = v.replace(/,$/, ''); - * v = v.split(','); - * for (var i = 0; i < v.length; i++) { - * if (!Ext.isNumeric(v[i])) { - * return 'Value must be a comma separated array of numbers'; - * } - * result.push(parseInt(v[i], 10)); - * } - * this.values = result; - * return true; - * }, - * listeners: { - * change: 'onTypeChange', - * buffer: 500, - * afterrender: { - * fn: 'afterTypeRender', - * single: true - * } - * } - * }, { - * reference: 'type', - * xtype: 'combobox', - * fieldLabel: 'Type', - * store: [ - * ['sparklineline', 'Line'], - * ['sparklinebox', 'Box'], - * ['sparklinebullet', 'Bullet'], - * ['sparklinediscrete', 'Discrete'], - * ['sparklinepie', 'Pie'], - * ['sparklinetristate', 'TriState'] - * ], - * value: 'sparklineline', - * listeners: { - * change: 'onTypeChange', - * buffer: 500 - * } - * }], - * - * // Start with a line plot. - * afterTypeRender: function(typeField) { - * typeField.setValue('6,10,4,-3,7,2'); - * }, - * - * onTypeChange: function() { - * var me = this, - * refs = me.getReferences(), - * config; - * - * if (me.sparkLine) { - * me.remove(me.sparkLine, true); - * } - * config = { - * xtype: refs.type.getValue(), - * values: refs.values.values, - * height: 25, - * width: 100 - * }; - * me.sparkLine = Ext.create(config); - * me.add(me.sparkLine); - * - * // Put under fields - * me.sparkLine.el.dom.style.marginLeft = refs.type.labelEl.getWidth() + 'px'; - * } - * }); - * - */ -Ext.define('Ext.sparkline.Base', { - extend: 'Ext.Widget', - requires: [ - 'Ext.XTemplate', - 'Ext.sparkline.CanvasCanvas', - 'Ext.sparkline.VmlCanvas' - ], - cachedConfig: { - baseCls: Ext.baseCSSPrefix + 'sparkline', - /** - * @cfg {String} [lineColor=#157fcc] The hex value for line colors in graphs which display lines ({@link Ext.sparkline.Box Box}, {@link Ext.sparkline.Discrete Discrete and {@link Ext.sparkline.Line Line}). - */ - lineColor: '#157fcc', - /** - * @cfg {String} [fillColor=#def] The hex value for fill color in graphs which fill areas ({@link Ext.sparkline.Line Line}). - */ - fillColor: '#def', - defaultPixelsPerValue: 3, - tagValuesAttribute: 'values', - enableTagOptions: false, - enableHighlight: true, - /** - * @cfg {String} [highlightColor=null] The hex value for the highlight color to use when mouseing over a graph segment. - */ - highlightColor: null, - /** - * @cfg {Number} [highlightLighten=1.4] How much to lighten the highlight color by when mouseing over a graph segment. - */ - highlightLighten: 1.4, - /** - * @cfg {Boolean} [tooltipSkipNull=true] Null values will not have a tooltip displayed. - */ - tooltipSkipNull: true, - /** - * @cfg {String} [tooltipPrefix] A string to prepend to each field displayed in a tooltip. - */ - tooltipPrefix: '', - /** - * @cfg {String} [tooltipSuffix] A string to append to each field displayed in a tooltip. - */ - tooltipSuffix: '', - /** - * @cfg {Boolean} [disableTooltips=false] Set to `true` to disable mouseover tooltips. - */ - disableTooltips: false, - disableInteraction: false, - /** - * @cfg {String/Ext.XTemplate} [tipTpl] An XTemplate used to display the value or values in a tooltip when hovering over a Sparkline. - * - * The implemented subclases all define their own `tipTpl`, but it can be overridden. - */ - tipTpl: null - }, - config: { - /** - * @cfg {Number[]} values An array of numbers which define the chart. - */ - values: null - }, - element: { - tag: 'canvas', - reference: 'element', - style: { - display: 'inline-block', - verticalAlign: 'top' - }, - listeners: { - mouseenter: 'onMouseEnter', - mouseleave: 'onMouseLeave', - mousemove: 'onMouseMove' - }, - // Create canvas zero sized so that it does not affect the containing element's initial layout - // https://sencha.jira.com/browse/EXTJSIV-10145 - width: 0, - height: 0 - }, - defaultBindProperty: 'values', - // When any config is changed, the canvas needs to be redrawn. - // This is done at the next animation frame when this queue is traversed. - redrawQueue: {}, - inheritableStatics: { - /** - * @private - * @static - * @inheritable - */ - sparkLineTipClass: Ext.baseCSSPrefix + 'sparkline-tip-target', - /** - * @private - * @static - * @inheritable - */ - onClassCreated: function(cls) { - var proto = cls.prototype, - configs = cls.getConfigurator().configs, - config; - // Set up an applier for all local configs which kicks off a request to redraw on the next animation frame - for (config in configs) { - // tipTpl not included in this scheme - if (config !== 'tipTpl') { - proto[Ext.Config.get(config).names.apply] = proto.applyConfigChange; - } - } - } - }, - constructor: function(config) { - var me = this; - // The canvas sets our element config - me.canvas = Ext.supports.Canvas ? new Ext.sparkline.CanvasCanvas(me) : new Ext.sparkline.VmlCanvas(me); - if (!me.getDisableTooltips()) { - me.element.cls = Ext.sparkline.Base.sparkLineTipClass; - } - Ext.apply(me, config); - me.callParent([ - config - ]); - // For compatibility of all the code. - me.el = me.element; - }, - // determine if all values of an array match a value - // returns true if the array is empty - all: function(val, arr, ignoreNull) { - var i; - for (i = arr.length; i--; ) { - if (ignoreNull && arr[i] === null) { - - continue; - } - if (arr[i] !== val) { - return false; - } - } - return true; - }, - // generic config value applier. - // Adds this to the queue to do a redraw on the next animation frame - applyConfigChange: function(newValue) { - var me = this; - me.redrawQueue[me.getId()] = me; - // Ensure that there is a single timer to handle all queued redraws. - if (!me.redrawTimer) { - Ext.sparkline.Base.prototype.redrawTimer = Ext.Function.requestAnimationFrame(me.processRedrawQueue); - } - return newValue; - }, - // Appliers convert an incoming config value. - // Ensure the tipTpl is an XTemplate - applyTipTpl: function(tipTpl) { - if (!tipTpl.isTemplate) { - tipTpl = new Ext.XTemplate(tipTpl); - } - return tipTpl; - }, - normalizeValue: function(val) { - var nf; - switch (val) { - case 'undefined': - val = undefined; - break; - case 'null': - val = null; - break; - case 'true': - val = true; - break; - case 'false': - val = false; - break; - default: - nf = parseFloat(val); - if (val == nf) { - val = nf; - }; - } - return val; - }, - normalizeValues: function(vals) { - var i, - result = []; - for (i = vals.length; i--; ) { - result[i] = this.normalizeValue(vals[i]); - } - return result; - }, - updateWidth: function(width, oldWidth) { - var me = this, - dom = me.element.dom; - me.callParent([ - width, - oldWidth - ]); - me.canvas.setWidth(width); - me.width = width; - if (me.height == null) { - me.setHeight(parseInt(me.measurer.getCachedStyle(dom.parentNode, 'line-height'), 10)); - } else { - me.redrawQueue[me.getId()] = me; - } - }, - updateHeight: function(height, oldHeight) { - var me = this; - me.callParent([ - height, - oldHeight - ]); - me.canvas.setHeight(height); - me.height = height; - me.redrawQueue[me.getId()] = me; - }, - updateValues: function(values) { - this.values = values; - }, - redraw: function() { - var me = this; - if (me.getValues()) { - me.onUpdate(); - me.canvas.onOwnerUpdate(); - me.renderGraph(); - } - }, - onUpdate: Ext.emptyFn, - /* - * Render the chart to the canvas - */ - renderGraph: function() { - var ret = true; - if (this.disabled) { - this.canvas.reset(); - ret = false; - } - return ret; - }, - onMouseEnter: function(e) { - this.onMouseMove(e); - }, - onMouseMove: function(e) { - this.tooltip.triggerEvent = e; - this.currentPageXY = e.getPoint(); - this.redraw(); - }, - onMouseLeave: function() { - var me = this; - me.currentPageXY = me.targetX = me.targetY = null; - me.redraw(); - me.tooltip.target = null; - me.tooltip.hide(); - }, - updateDisplay: function() { - var me = this, - values = me.getValues(), - offset, - tooltip = me.tooltip, - tooltipHTML, region; - if (values && values.length && me.currentPageXY && me.el.getRegion().contains(me.currentPageXY)) { - offset = me.canvas.el.getXY(); - region = me.getRegion(me.currentPageXY[0] - offset[0], me.currentPageXY[1] - offset[1]); - if (region != null && region < values.length) { - if (!me.disableHighlight) { - me.renderHighlight(region); - } - tooltipHTML = me.getRegionTooltip(region); - } - me.fireEvent('sparklineregionchange', me); - if (tooltipHTML) { - if (!me.lastTooltipHTML || tooltipHTML[0] !== me.lastTooltipHTML[0] || tooltipHTML[1] !== me.lastTooltipHTML[1]) { - tooltip.setTitle(tooltipHTML[0]); - tooltip.update(tooltipHTML[1]); - me.lastTooltipHTML = tooltipHTML; - } - tooltip.target = me.el; - tooltip.onTargetOver(tooltip.triggerEvent); - } - } - // No tip content; ensure it's hidden - if (!tooltipHTML) { - tooltip.target = null; - tooltip.hide(); - } - }, - /* - * Return a region id for a given x/y co-ordinate - */ - getRegion: Ext.emptyFn, - /* - * Fetch the HTML to display as a tooltip - */ - getRegionTooltip: function(region) { - var me = this, - header = me.tooltipChartTitle, - entries = [], - tipTpl = me.getTipTpl(), - fields, showFields, showFieldsKey, newFields, fv, formatter, fieldlen, i, j; - fields = me.getRegionFields(region); - formatter = me.tooltipFormatter; - if (formatter) { - return formatter(me, me, fields); - } - if (!tipTpl) { - return ''; - } - if (!Ext.isArray(fields)) { - fields = [ - fields - ]; - } - showFields = me.tooltipFormatFieldlist; - showFieldsKey = me.tooltipFormatFieldlistKey; - if (showFields && showFieldsKey) { - // user-selected ordering of fields - newFields = []; - for (i = fields.length; i--; ) { - fv = fields[i][showFieldsKey]; - if ((j = Ext.Array.indexOf(fv, showFields)) !== -1) { - newFields[j] = fields[i]; - } - } - fields = newFields; - } - fieldlen = fields.length; - for (j = 0; j < fieldlen; j++) { - if (!fields[j].isNull || !me.getTooltipSkipNull()) { - Ext.apply(fields[j], { - prefix: me.getTooltipPrefix(), - suffix: me.getTooltipSuffix() - }); - entries.push(tipTpl.apply(fields[j])); - } - } - if (header || entries.length) { - return [ - header, - entries.join('
    ') - ]; - } - return ''; - }, - getRegionFields: Ext.emptyFn, - calcHighlightColor: function(color) { - var me = this, - highlightColor = me.getHighlightColor(), - lighten = me.getHighlightLighten(), - parse, mult, rgbnew, i; - if (highlightColor) { - return highlightColor; - } - if (lighten) { - // extract RGB values - parse = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(color) || /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(color); - if (parse) { - rgbnew = []; - mult = color.length === 4 ? 16 : 1; - for (i = 0; i < 3; i++) { - rgbnew[i] = Ext.Number.constrain(Math.round(parseInt(parse[i + 1], 16) * mult * lighten), 0, 255); - } - return 'rgb(' + rgbnew.join(',') + ')'; - } - } - return color; - }, - destroy: function() { - delete this.redrawQueue[this.getId()]; - this.callParent(); - } -}, function(SparklineBase) { - var proto = SparklineBase.prototype; - Ext.onInternalReady(function() { - proto.tooltip = new Ext.tip.ToolTip({ - id: 'sparklines-tooltip', - showDelay: 0, - dismissDelay: 0, - hideDelay: 400 - }); - }); - SparklineBase.onClassCreated(SparklineBase); - proto.processRedrawQueue = function() { - var redrawQueue = proto.redrawQueue, - id; - for (id in redrawQueue) { - redrawQueue[id].redraw(); - } - proto.redrawQueue = {}; - proto.redrawTimer = 0; - }; - // If we are on a VML platform (IE8 - TODO: remove this when that retires)... - if (!Ext.supports.Canvas) { - SparklineBase.prototype.element = { - tag: 'span', - reference: 'element', - listeners: { - mouseenter: 'onMouseEnter', - mouseleave: 'onMouseLeave', - mousemove: 'onMouseMove' - }, - style: { - display: 'inline-block', - position: 'relative', - overflow: 'hidden', - margin: '0px', - padding: '0px', - verticalAlign: 'top', - cursor: 'default' - }, - children: [ - { - tag: 'svml:group', - reference: 'groupEl', - coordorigin: '0 0', - coordsize: '0 0', - style: 'position:absolute;width:0;height:0;pointer-events:none' - } - ] - }; - } -}); - -/** - * Base class for bar highlighting - */ -Ext.define('Ext.sparkline.BarBase', { - extend: 'Ext.sparkline.Base', - renderHighlight: function(region) { - this.renderRegion(region, true); - }, - renderGraph: function() { - var me = this, - values = me.values, - canvas = me.canvas, - regionShapes = me.regionShapes || (me.regionShapes = {}), - shapes, ids, i, j; - if (!me.callParent()) { - return; - } - for (i = values.length; i--; ) { - shapes = me.renderRegion(i); - if (shapes) { - if (Ext.isArray(shapes)) { - ids = []; - for (j = shapes.length; j--; ) { - shapes[j].append(); - ids.push(shapes[j].id); - } - regionShapes[i] = ids; - } else { - shapes.append(); - regionShapes[i] = shapes.id; - } - } else // store just the shapeid - { - // null value - regionShapes[i] = null; - } - } - // If mouse is over, add the highlight sprite - if (me.currentPageXY) { - me.currentRegion = null; - me.updateDisplay(); - } - canvas.render(); - } -}); - -/** - * Base class for Range Map - */ -Ext.define('Ext.sparkline.RangeMap', { - constructor: function(map) { - var key, range, - rangelist = []; - for (key in map) { - if (map.hasOwnProperty(key) && typeof key === 'string' && key.indexOf(':') > -1) { - range = key.split(':'); - range[0] = range[0].length === 0 ? -Infinity : parseFloat(range[0]); - range[1] = range[1].length === 0 ? Infinity : parseFloat(range[1]); - range[2] = map[key]; - rangelist.push(range); - } - } - this.map = map; - this.rangelist = rangelist || false; - }, - get: function(value) { - var rangelist = this.rangelist, - i, range, result; - if ((result = this.map[value]) !== undefined) { - return result; - } - if (rangelist) { - for (i = rangelist.length; i--; ) { - range = rangelist[i]; - if (range[0] <= value && range[1] >= value) { - return range[2]; - } - } - } - } -}); - -/** - * @class Ext.sparkline.Bar - * - * Plots a bar chart of the values in the passed {@link #values} array. - * - * See {@link Ext.sparkline.Base the base class} for a simple example. - */ -Ext.define('Ext.sparkline.Bar', { - extend: 'Ext.sparkline.BarBase', - requires: [ - 'Ext.sparkline.RangeMap' - ], - alias: 'widget.sparklinebar', - config: { - /** - * @cfg {String} [barColor=#3366cc] The bar color for positive values. - */ - barColor: '#3366cc', - /** - * @cfg {String} [negBarColor=#f44] The bar color for negative values. - */ - negBarColor: '#f44', - /** - * @cfg {String[]} [stackedBarColor] An array of colours to use for stacked bar charts. - * The first series will use the first value in the array, the second series will use the second, etc. - */ - stackedBarColor: [ - '#3366cc', - '#dc3912', - '#ff9900', - '#109618', - '#66aa00', - '#dd4477', - '#0099c6', - '#990099' - ], - /** - * @cfg {String} [zeroColor] The bar color for zero values. - */ - zeroColor: null, - /** - * @cfg {String} [nullColor] The bar color for null values. Usually null values are omitted and not plotted. Setting - * this config causes a very thin bar to be plotted with the special color in the case that null is a meaningful value in the series. - */ - nullColor: null, - /** - * @cfg {Boolean} [zeroAxis=true] Centers the Y axis at zero by default. - */ - zeroAxis: true, - /** - * @cfg {Number} [barWidth=4] The pixel width of bars. - */ - barWidth: 4, - /** - * @cfg {Number} [barSpacing=1] The pixel spacing between bars. - */ - barSpacing: 1, - /** - * @cfg {Number} [chartRangeMin] The minimum value to use for the range of Y values of the chart - Defaults to the minimum value supplied. - */ - chartRangeMin: null, - /** - * @cfg {Number} [chartRangeMax] The maximum value to use for the range of Y values of the chart - Defaults to the minimum value supplied. - */ - chartRangeMax: null, - /** - * @cfg {Boolean} chartRangeClip If true then the y values supplied to plot will be clipped to fall - * between {@link #chartRangeMin} and {@link #chartRangeMax} - By default chartRangeMin/Max just ensure that the chart - * spans at least that range of values, but does not constrain it. - */ - chartRangeClip: false, - /** - * @inheritdoc Ext.sparkline.TriState - */ - colorMap: null, - tipTpl: new Ext.XTemplate('● {prefix}{value}{suffix}') - }, - remove: function(vals, filter) { - var i, vl, - result = []; - for (i = 0 , vl = vals.length; i < vl; i++) { - if (vals[i] !== filter) { - result.push(vals[i]); - } - } - return result; - }, - // determine if all values of an array match a value - // returns true if the array is empty - all: function(arr, val, ignoreNull) { - var i; - for (i = arr.length; i--; ) { - if (ignoreNull && arr[i] === null) { - - continue; - } - if (arr[i] !== val) { - return false; - } - } - return true; - }, - applyColorMap: function(colorMap) { - var me = this; - if (Ext.isArray(colorMap)) { - me.colorMapByIndex = colorMap; - me.colorMapByValue = null; - } else { - me.colorMapByIndex = null; - me.colorMapByValue = colorMap; - if (me.colorMapByValue && me.colorMapByValue.get == null) { - me.colorMapByValue = new Ext.sparkline.RangeMap(colorMap); - } - } - return colorMap; - }, - onUpdate: function() { - var me = this, - values = me.values, - barWidth = me.getBarWidth(), - barSpacing = me.getBarSpacing(), - chartRangeMin = me.getChartRangeMin(), - chartRangeMax = me.getChartRangeMax(), - chartRangeClip = me.getChartRangeClip(), - stackMin = Infinity, - stackMax = -Infinity, - isStackString, groupMin, groupMax, stackRanges, numValues, i, vlen, range, - zeroAxis = me.getZeroAxis(), - xAxisOffset, min, max, clipMin, clipMax, stacked, vlist, j, slen, svals, val, yoffset, yMaxCalc, - stackTotals = [], - stackRangesNeg = []; - // scan values to determine whether to stack bars - for (i = 0 , vlen = values.length; i < vlen; i++) { - val = values[i]; - isStackString = typeof (val) === 'string' && val.indexOf(':') > -1; - if (isStackString || Ext.isArray(val)) { - stacked = true; - if (isStackString) { - val = values[i] = me.normalizeValues(val.split(':')); - } - val = me.remove(val, null); - // min/max will treat null as zero - groupMin = Math.min.apply(Math, val); - groupMax = Math.max.apply(Math, val); - if (groupMin < stackMin) { - stackMin = groupMin; - } - if (groupMax > stackMax) { - stackMax = groupMax; - } - } - } - me.stacked = stacked; - me.regionShapes = {}; - me.totalBarWidth = barWidth + barSpacing; - me.width = (values.length * barWidth) + ((values.length - 1) * barSpacing); - if (chartRangeClip) { - clipMin = chartRangeMin == null ? -Infinity : chartRangeMin; - clipMax = chartRangeMax == null ? Infinity : chartRangeMax; - } - numValues = []; - stackRanges = stacked ? [] : numValues; - for (i = 0 , vlen = values.length; i < vlen; i++) { - if (stacked) { - vlist = values[i]; - values[i] = svals = []; - stackTotals[i] = 0; - stackRanges[i] = stackRangesNeg[i] = 0; - for (j = 0 , slen = vlist.length; j < slen; j++) { - val = svals[j] = chartRangeClip ? Ext.Number.constrain(vlist[j], clipMin, clipMax) : vlist[j]; - if (val !== null) { - if (val > 0) { - stackTotals[i] += val; - } - if (stackMin < 0 && stackMax > 0) { - if (val < 0) { - stackRangesNeg[i] += Math.abs(val); - } else { - stackRanges[i] += val; - } - } else { - stackRanges[i] += Math.abs(val - (val < 0 ? stackMax : stackMin)); - } - numValues.push(val); - } - } - } else { - val = chartRangeClip ? Ext.Number.constrain(values[i], clipMin, clipMax) : values[i]; - val = values[i] = me.normalizeValue(val); - if (val !== null) { - numValues.push(val); - } - } - } - me.max = max = Math.max.apply(Math, numValues); - me.min = min = Math.min.apply(Math, numValues); - me.stackMax = stackMax = stacked ? Math.max.apply(Math, stackTotals) : max; - me.stackMin = stackMin = stacked ? Math.min.apply(Math, numValues) : min; - if (chartRangeMin != null && (chartRangeClip || chartRangeMin < min)) { - min = chartRangeMin; - } - if (chartRangeMax != null && (chartRangeClip || chartRangeMax > max)) { - max = chartRangeMax; - } - if (min <= 0 && max >= 0 && zeroAxis) { - xAxisOffset = 0; - } else if (!zeroAxis) { - xAxisOffset = min; - } else if (min > 0) { - xAxisOffset = min; - } else { - xAxisOffset = max; - } - me.xAxisOffset = xAxisOffset; - range = stacked ? (Math.max.apply(Math, stackRanges) + Math.max.apply(Math, stackRangesNeg)) : max - min; - // as we plot zero/min values a single pixel line, we add a pixel to all other - // values - Reduce the effective canvas size to suit - me.canvasHeightEf = (zeroAxis && min < 0) ? me.getHeight() - 2 : me.getHeight() - 1; - if (min < xAxisOffset) { - yMaxCalc = (stacked && max >= 0) ? stackMax : max; - yoffset = (yMaxCalc - xAxisOffset) / range * me.getHeight(); - if (yoffset !== Math.ceil(yoffset)) { - me.canvasHeightEf -= 2; - yoffset = Math.ceil(yoffset); - } - } else { - yoffset = me.getHeight(); - } - me.yoffset = yoffset; - me.range = range; - }, - getRegion: function(x, y) { - var result = Math.floor(x / this.totalBarWidth); - return (result < 0 || result >= this.values.length) ? undefined : result; - }, - getRegionFields: function(region) { - var values = Ext.Array.from(this.values[region]), - result = [], - value, i; - for (i = values.length; i--; ) { - value = values[i]; - result.push({ - isNull: value === null, - value: value, - color: this.calcColor(i, value, region), - offset: region - }); - } - return result; - }, - calcColor: function(stacknum, value, valuenum) { - var me = this, - colorMapByIndex = me.colorMapByIndex, - colorMapByValue = me.colorMapByValue, - color, newColor, - zeroColor = me.getZeroColor(); - if (this.stacked) { - color = me.getStackedBarColor(); - } else { - color = (value < 0) ? me.getNegBarColor() : me.getBarColor(); - } - if (value === 0 && zeroColor !== undefined) { - color = zeroColor; - } - if (colorMapByValue && (newColor = colorMapByValue.get(value))) { - color = newColor; - } else if (colorMapByIndex && colorMapByIndex.length > valuenum) { - color = colorMapByIndex[valuenum]; - } - return Ext.isArray(color) ? color[stacknum % color.length] : color; - }, - /* - * Render bar(s) for a region - */ - renderRegion: function(valuenum, highlight) { - var me = this, - vals = me.values[valuenum], - xaxisOffset = me.xAxisOffset, - range = me.range, - stacked = me.stacked, - canvas = me.canvas, - barWidth = me.getBarWidth(), - x = valuenum * me.totalBarWidth, - canvasHeightEf = me.canvasHeightEf, - yoffset = me.yoffset, - y, height, color, isNull, yoffsetNeg, i, valcount, val, minPlotted, allMin, - nullColor = me.getNullColor(); - vals = Ext.isArray(vals) ? vals : [ - vals - ]; - valcount = vals.length; - val = vals[0]; - isNull = me.all(vals, null); - allMin = me.all(vals, xaxisOffset, true); - if (isNull) { - if (nullColor) { - color = highlight ? nullColor : me.calcHighlightColor(nullColor, me); - y = (yoffset > 0) ? yoffset - 1 : yoffset; - canvas.drawRect(x, y, barWidth - 1, 0, color, color).append(); - } - return; - } - yoffsetNeg = yoffset; - for (i = 0; i < valcount; i++) { - val = vals[i]; - if (stacked && val === xaxisOffset) { - if (!allMin || minPlotted) { - - continue; - } - minPlotted = true; - } - if (range > 0) { - height = Math.floor(canvasHeightEf * ((Math.abs(val - xaxisOffset) / range))) + 1; - } else { - height = 1; - } - if (val < xaxisOffset || (val === xaxisOffset && yoffset === 0)) { - y = yoffsetNeg; - yoffsetNeg += height; - } else { - y = yoffset - height; - yoffset -= height; - } - color = me.calcColor(i, val, valuenum); - if (highlight) { - color = me.calcHighlightColor(color, me); - } - canvas.drawRect(x, y, barWidth - 1, height - 1, color, color).append(); - } - } -}, function(cls) { - cls.onClassCreated(cls); -}); - -/** - * @class Ext.sparkline.Box - * Generates a box plot graph from the provided {@link #values} array. - * - * See Wikipedia Box Plots - * - * See {@link Ext.sparkline.Base the base class} for a simple example. - */ -Ext.define('Ext.sparkline.Box', { - extend: 'Ext.sparkline.Base', - alias: 'widget.sparklinebox', - config: { - /** - * @cfg {Boolean} [raw=false] By default the points are calciulated from the input values array. Set this to true to passthe pre-calculated points in the values config. - */ - raw: false, - /** - * @cfg {String} [boxLineColor=#000] The color of the box outline. - */ - boxLineColor: '#000', - /** - * @cfg {String} [boxFillColor=#cdf] The color of the box fill. - */ - boxFillColor: '#cdf', - /** - * @cfg {String} [whiskerColor=#000] The color of the whiskers. - */ - whiskerColor: '#000', - /** - * @cfg {String} [outlierLineColor=#333] The color of the outlier circles' outline. - */ - outlierLineColor: '#333', - /** - * @cfg {String} [outlierFillColor=#fff] The fill color of the outlier circles. - */ - outlierFillColor: '#fff', - /** - * @cfg {String} [medianColor=#f00] The color of the median line. - */ - medianColor: '#f00', - /** - * @cfg {Boolean} [showOutliers=true] Configure as `false` to not show outlier circles. - */ - showOutliers: true, - /** - * @cfg {Number} [outlierIQR=1.5] The inter-quartile range multipler used to calculate values that qualify as an outlier. - */ - outlierIQR: 1.5, - /** - * @cfg {Number} [spotRadius=1.5] The radius of the outlier circles. - */ - spotRadius: 1.5, - /** - * @cfg {Number} [target] If set, a crosshair will be drawn at the specified value point. - */ - target: null, - /** - * @cfg {Number} [targetColor=#4a2] The color of the crosshair drawn at the pointe specified by {@link #target}. - */ - targetColor: '#4a2', - /** - * @cfg {Number} [chartRangeMin] The minimum value to use for the range of Y values of the chart - Defaults to the minimum value supplied. - */ - chartRangeMin: null, - /** - * @cfg {Number} [chartRangeMax] The maximum value to use for the range of Y values of the chart - Defaults to the minimum value supplied. - */ - chartRangeMax: null, - tipTpl: new Ext.XTemplate('{field:this.fields}: {value}', { - fields: function(v) { - var fields = { - lq: 'Lower Quartile', - med: 'Median', - uq: 'Upper Quartile', - lo: 'Left Outlier', - ro: 'Right Outlier', - lw: 'Left Whisker', - rw: 'Right Whisker' - }; - return fields[v]; - } - }), - tooltipFormatFieldlistKey: 'field' - }, - quartile: function(values, q) { - var vl; - if (q === 2) { - vl = Math.floor(values.length / 2); - return values.length % 2 ? values[vl] : (values[vl - 1] + values[vl]) / 2; - } else { - if (values.length % 2) { - // odd - vl = (values.length * q + q) / 4; - return vl % 1 ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 2 : values[vl - 1]; - } else { - //even - vl = (values.length * q + 2) / 4; - return vl % 1 ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 2 : values[vl - 1]; - } - } - }, - // Ensure values is an array of numbers - applyValues: function(newValues) { - newValues = Ext.Array.map(Ext.Array.from(newValues), Number); - this.disabled = !(newValues && newValues.length); - return newValues; - }, - /* - * Simulate a single region - */ - getRegion: function() { - return 1; - }, - getRegionFields: function() { - var result = [ - { - field: 'lq', - value: this.quartiles[0] - }, - { - field: 'med', - value: this.quartiles[1] - }, - { - field: 'uq', - value: this.quartiles[2] - } - ]; - if (this.loutlier !== undefined) { - result.push({ - field: 'lo', - value: this.loutlier - }); - } - if (this.routlier !== undefined) { - result.push({ - field: 'ro', - value: this.routlier - }); - } - if (this.lwhisker !== undefined) { - result.push({ - field: 'lw', - value: this.lwhisker - }); - } - if (this.rwhisker !== undefined) { - result.push({ - field: 'rw', - value: this.rwhisker - }); - } - return result; - }, - renderHighlight: Ext.emptyFn, - renderGraph: function() { - var me = this, - canvas = me.canvas, - values = me.values, - vlen = values.length, - canvasWidth = me.getWidth(), - canvasHeight = me.getHeight(), - chartRangeMin = me.getChartRangeMin(), - chartRangeMax = me.getChartRangeMax(), - minValue = chartRangeMin == null ? Math.min.apply(Math, values) : chartRangeMin, - maxValue = chartRangeMax == null ? Math.max.apply(Math, values) : chartRangeMax, - canvasLeft = 0, - lwhisker, loutlier, iqr, q1, q2, q3, rwhisker, routlier, i, size, unitSize, - spotRadius = me.getSpotRadius(), - outlierLineColor = me.getOutlierLineColor(), - outlierFillColor = me.getOutlierFillColor(), - showOutliers = me.getShowOutliers(), - outlierIQR = me.getOutlierIQR(), - lineColor = me.getLineColor(), - whiskerColor = me.getWhiskerColor(), - targetColor = me.getTargetColor(); - if (!me.callParent()) { - return; - } - if (me.raw) { - if (showOutliers && values.length > 5) { - loutlier = values[0]; - lwhisker = values[1]; - q1 = values[2]; - q2 = values[3]; - q3 = values[4]; - rwhisker = values[5]; - routlier = values[6]; - } else { - lwhisker = values[0]; - q1 = values[1]; - q2 = values[2]; - q3 = values[3]; - rwhisker = values[4]; - } - } else { - values.sort(function(a, b) { - return a - b; - }); - q1 = me.quartile(values, 1); - q2 = me.quartile(values, 2); - q3 = me.quartile(values, 3); - iqr = q3 - q1; - if (showOutliers) { - lwhisker = rwhisker = null; - for (i = 0; i < vlen; i++) { - if (lwhisker == null && values[i] > q1 - (iqr * outlierIQR)) { - lwhisker = values[i]; - } - if (values[i] < q3 + (iqr * outlierIQR)) { - rwhisker = values[i]; - } - } - loutlier = values[0]; - routlier = values[vlen - 1]; - } else { - lwhisker = values[0]; - rwhisker = values[vlen - 1]; - } - } - me.quartiles = [ - q1, - q2, - q3 - ]; - me.lwhisker = lwhisker; - me.rwhisker = rwhisker; - me.loutlier = loutlier; - me.routlier = routlier; - unitSize = canvasWidth / (maxValue - minValue + 1); - if (showOutliers) { - canvasLeft = Math.ceil(spotRadius); - canvasWidth -= 2 * Math.ceil(spotRadius); - unitSize = canvasWidth / (maxValue - minValue + 1); - if (loutlier < lwhisker) { - canvas.drawCircle((loutlier - minValue) * unitSize + canvasLeft, canvasHeight / 2, spotRadius, outlierLineColor, outlierFillColor).append(); - } - if (routlier > rwhisker) { - canvas.drawCircle((routlier - minValue) * unitSize + canvasLeft, canvasHeight / 2, spotRadius, outlierLineColor, outlierFillColor).append(); - } - } - // box - canvas.drawRect(Math.round((q1 - minValue) * unitSize + canvasLeft), Math.round(canvasHeight * 0.1), Math.round((q3 - q1) * unitSize), Math.round(canvasHeight * 0.8), me.getBoxLineColor(), me.getBoxFillColor()).append(); - // left whisker - canvas.drawLine(Math.round((lwhisker - minValue) * unitSize + canvasLeft), Math.round(canvasHeight / 2), Math.round((q1 - minValue) * unitSize + canvasLeft), Math.round(canvasHeight / 2), lineColor).append(); - canvas.drawLine(Math.round((lwhisker - minValue) * unitSize + canvasLeft), Math.round(canvasHeight / 4), Math.round((lwhisker - minValue) * unitSize + canvasLeft), Math.round(canvasHeight - canvasHeight / 4), whiskerColor).append(); - // right whisker - canvas.drawLine(Math.round((rwhisker - minValue) * unitSize + canvasLeft), Math.round(canvasHeight / 2), Math.round((q3 - minValue) * unitSize + canvasLeft), Math.round(canvasHeight / 2), lineColor).append(); - canvas.drawLine(Math.round((rwhisker - minValue) * unitSize + canvasLeft), Math.round(canvasHeight / 4), Math.round((rwhisker - minValue) * unitSize + canvasLeft), Math.round(canvasHeight - canvasHeight / 4), whiskerColor).append(); - // median line - canvas.drawLine(Math.round((q2 - minValue) * unitSize + canvasLeft), Math.round(canvasHeight * 0.1), Math.round((q2 - minValue) * unitSize + canvasLeft), Math.round(canvasHeight * 0.9), me.getMedianColor()).append(); - if (me.target) { - size = Math.ceil(me.spotRadius); - canvas.drawLine(Math.round((me.target - minValue) * unitSize + canvasLeft), Math.round((canvasHeight / 2) - size), Math.round((me.target - minValue) * unitSize + canvasLeft), Math.round((canvasHeight / 2) + size), targetColor).append(); - canvas.drawLine(Math.round((me.target - minValue) * unitSize + canvasLeft - size), Math.round(canvasHeight / 2), Math.round((me.target - minValue) * unitSize + canvasLeft + size), Math.round(canvasHeight / 2), targetColor).append(); - } - // If mouse is over, re-apply the highlight - if (me.currentPageXY && me.el.getRegion().contains(me.currentPageXY)) { - me.currentRegion = null; - me.updateDisplay(); - } - canvas.render(); - } -}); - -/** - * @class Ext.sparkline.Bullet - * - * Plots a bullet graph based upon the input {@link #values} array. - * - * See Bullet graphs Wikipedia Page for more information. - * - * See {@link Ext.sparkline.Base the base class} for a simple example. - */ -Ext.define('Ext.sparkline.Bullet', { - extend: 'Ext.sparkline.Base', - alias: 'widget.sparklinebullet', - config: { - /** - * @cfg {String} [targetColor=#f33] The colour of the vertical target marker. - */ - targetColor: '#f33', - /** - * @cfg {Number} [targetWidth=3] Width of the target bar in pixels. - */ - targetWidth: 3, - /** - * @cfg {String} [performanceColor=#33f] The color of the performance measure horizontal bar. - */ - performanceColor: '#33f', - /** - * @cfg {String[]} [rangeColors] An array of colors to use for each qualitative range background color. - */ - rangeColors: [ - '#d3dafe', - '#a8b6ff', - '#7f94ff' - ], - /** - * @cfg {Number} [base] Set this to a number to change the base start number. - */ - base: null, - tipTpl: new Ext.XTemplate('{fieldkey:this.fields} - {value}', { - fields: function(v) { - if (v === 'r') { - return 'Range'; - } - if (v === 'p') { - return 'Performance'; - } - if (v === 't') { - return 'Target'; - } - } - }) - }, - // Ensure values is an array of normalized values - applyValues: function(newValues) { - newValues = Ext.Array.map(Ext.Array.from(newValues), this.normalizeValue); - this.disabled = !(newValues && newValues.length); - return newValues; - }, - onUpdate: function() { - var me = this, - values = me.values, - min, max, vals, - base = me.getBase(); - me.callParent(arguments); - // target or performance could be null - vals = values.slice(); - vals[0] = vals[0] === null ? vals[2] : vals[0]; - vals[1] = values[1] === null ? vals[2] : vals[1]; - min = Math.min.apply(Math, values); - max = Math.max.apply(Math, values); - if (base == null) { - min = min < 0 ? min : 0; - } else { - min = base; - } - me.min = min; - me.max = max; - me.range = max - min; - me.shapes = {}; - me.valueShapes = {}; - me.regiondata = {}; - if (!values.length) { - me.disabled = true; - } - }, - getRegion: function(x, y) { - var shapeid = this.canvas.getShapeAt(x, y); - return (shapeid !== undefined && this.shapes[shapeid] !== undefined) ? this.shapes[shapeid] : undefined; - }, - getRegionFields: function(region) { - return { - fieldkey: region.substr(0, 1), - value: this.values[region.substr(1)], - region: region - }; - }, - renderHighlight: function(region) { - switch (region.substr(0, 1)) { - case 'r': - this.renderRange(region.substr(1), true).append(); - break; - case 'p': - this.renderPerformance(true).append(); - break; - case 't': - this.renderTarget(true).append(); - break; - } - }, - renderRange: function(region, highlight) { - var rangeval = this.values[region], - rangewidth = Math.round(this.getWidth() * ((rangeval - this.min) / this.range)), - color = this.getRangeColors()[region - 2]; - if (highlight) { - color = this.calcHighlightColor(color); - } - return this.canvas.drawRect(0, 0, rangewidth - 1, this.getHeight() - 1, color, color); - }, - renderPerformance: function(highlight) { - var perfval = this.values[1], - perfwidth = Math.round(this.getWidth() * ((perfval - this.min) / this.range)), - color = this.getPerformanceColor(); - if (highlight) { - color = this.calcHighlightColor(color); - } - return this.canvas.drawRect(0, Math.round(this.getHeight() * 0.3), perfwidth - 1, Math.round(this.getHeight() * 0.4) - 1, color, color); - }, - renderTarget: function(highlight) { - var targetval = this.values[0], - targetWidth = this.getTargetWidth(), - x = Math.round(this.getWidth() * ((targetval - this.min) / this.range) - (targetWidth / 2)), - targettop = Math.round(this.getHeight() * 0.1), - targetheight = this.getHeight() - (targettop * 2), - color = this.getTargetColor(); - if (highlight) { - color = this.calcHighlightColor(color); - } - return this.canvas.drawRect(x, targettop, targetWidth - 1, targetheight - 1, color, color); - }, - renderGraph: function() { - var me = this, - vlen = me.values.length, - canvas = me.canvas, - i, shape, - shapes = me.shapes || (me.shapes = {}), - valueShapes = me.valueShapes || (me.valueShapes = {}); - if (!me.callParent()) { - return; - } - for (i = 2; i < vlen; i++) { - shape = me.renderRange(i).append(); - shapes[shape.id] = 'r' + i; - valueShapes['r' + i] = shape.id; - } - if (me.values[1] !== null) { - shape = me.renderPerformance().append(); - shapes[shape.id] = 'p1'; - valueShapes.p1 = shape.id; - } - if (me.values[0] !== null) { - shape = this.renderTarget().append(); - shapes[shape.id] = 't0'; - valueShapes.t0 = shape.id; - } - // If mouse is over, apply the highlight - if (me.currentPageXY && me.el.getRegion().contains(me.currentPageXY)) { - me.updateDisplay(); - } - canvas.render(); - } -}); - -/** - * @class Ext.sparkline.Discrete - * - * Plots a series of thin vertical lines based upon the input {@link #values} array. - * - * See {@link Ext.sparkline.Base the base class} for a simple example. - */ -Ext.define('Ext.sparkline.Discrete', { - extend: 'Ext.sparkline.BarBase', - alias: 'widget.sparklinediscrete', - config: { - /** - * @cfg {Number} lineHeight Height of each line in pixels - Defaults to 30% of the graph height. - */ - lineHeight: 'auto', - /** - * @cfg {String} thresholdColor Colour to use in combination with {@link #thresholdValue} - */ - thresholdColor: null, - /** - * @cfg {Number} thresholdValue Values less than this value will be drawn using {@link #thresholdColor} instead of lineColor - */ - thresholdValue: 0, - /** - * @cfg {Number} [chartRangeMax] The maximum value to use for the range of Y values of the chart - Defaults to the maximum value supplied. - */ - chartRangeMax: null, - /** - * @cfg {Number} [chartRangeMin] The minimum value to use for the range of Y values of the chart - Defaults to the minimum value supplied. - */ - chartRangeMin: null, - /** - * @cfg {Boolean} chartRangeClip If true then the y values supplied to plot will be clipped to fall - * between {@link #chartRangeMin} and {@link #chartRangeMax} - By default chartRangeMin/Max just ensure that the chart - * spans at least that range of values, but does not constrain it. - */ - chartRangeClip: false, - tipTpl: new Ext.XTemplate('{prefix}{value}{suffix}') - }, - // Ensure values is an array of numbers - applyValues: function(newValues) { - newValues = Ext.Array.map(Ext.Array.from(newValues), Number); - this.disabled = !(newValues && newValues.length); - return newValues; - }, - onUpdate: function() { - var me = this, - values = me.values, - chartRangeMin = me.getChartRangeMin(), - chartRangeMax = me.getChartRangeMax(), - chartRangeClip = me.getChartRangeClip(); - me.callParent(arguments); - me.regionShapes = {}; - me.min = Math.min.apply(Math, values); - me.max = Math.max.apply(Math, values); - me.range = me.max - me.min; - me.width = me.getWidth(); - me.interval = Math.floor(me.width / values.length); - me.itemWidth = me.width / values.length; - if (chartRangeMin != null && (chartRangeClip || chartRangeMin < me.min)) { - me.min = chartRangeMin; - } - if (chartRangeMax != null && (chartRangeClip || chartRangeMax > me.max)) { - me.max = chartRangeMax; - } - if (me.canvas) { - if (me.getLineHeight() === 'auto') { - me.setLineHeight(Math.round(me.getHeight() * 0.3)); - } - } - }, - getRegion: function(x, y) { - return Math.floor(x / this.itemWidth); - }, - getRegionFields: function(region) { - return { - isNull: this.values[region] === undefined, - value: this.values[region], - offset: region - }; - }, - renderRegion: function(valuenum, highlight) { - var me = this, - values = me.values, - min = me.min, - max = me.max, - range = me.range, - interval = me.interval, - canvas = me.canvas, - canvasHeight = me.getHeight(), - lineHeight = me.getLineHeight(), - pheight = canvasHeight - lineHeight, - ytop, val, color, x, - thresholdColor = me.getThresholdColor(); - val = Ext.Number.constrain(values[valuenum], min, max); - x = valuenum * interval; - ytop = Math.round(pheight - pheight * ((val - min) / range)); - color = (thresholdColor && val < me.getThresholdValue()) ? thresholdColor : me.getLineColor(); - if (highlight) { - color = me.calcHighlightColor(color); - } - canvas.drawLine(x, ytop, x, ytop + lineHeight, color).append(); - } -}); - -/** - * @class Ext.sparkline.Line - * - * Plots a line graph based upon the input {@link #values} array. - * - * See {@link Ext.sparkline.Base the base class} for a simple example. - */ -Ext.define('Ext.sparkline.Line', { - extend: 'Ext.sparkline.Base', - requires: [ - 'Ext.sparkline.RangeMap' - ], - alias: 'widget.sparklineline', - config: { - /** - * @cfg {String} [spotColor=#f80] The colour of the final value marker. Set to false or an empty string to hide it. - */ - spotColor: '#f80', - /** - * @cfg {String} [highlightSpotColor=#5f5] The colour of value marker spots when mouseovered. - */ - highlightSpotColor: '#5f5', - /** - * @cfg {String} [highlightLineColor=#f22] The colour of value line shown when the graph is mouseovered. - */ - highlightLineColor: '#f22', - /** - * @cfg {Number} [spotRadius=1.5] The pixel radius of min, max and final value dots. - */ - spotRadius: 1.5, - /** - * @cfg {String} [minSpotColor=#f80] The colour of the mimimum value marker. Set to false or an empty string to hide it. - */ - minSpotColor: '#f80', - /** - * @cfg {String} [maxSpotColor=#f80] The colour of the maximum value marker. Set to false or an empty string to hide it. - */ - maxSpotColor: '#f80', - /** - * @cfg {Number} [lineWidth=1] The pixel width of the line plotted. - */ - lineWidth: 1, - /** - * @cfg {Number} [normalRangeMin] See {@link #normalRangeMax} The minimum value to overlay a "normal range bar" over the graph using the {@link #normalRangeColor}. - */ - normalRangeMin: null, - /** - * @cfg {Number} [normalRangeMax] See {@link #normalRangeMin} The maximum value to overlay a "normal range bar" over the graph using the {@link #normalRangeColor}. - */ - normalRangeMax: null, - /** - * @cfg {String} [normalRangeColor=#ccc] See {@link #normalRangeMin} and {@link #normalRangeMax} The color of the undererlayed "normal range bar". - */ - normalRangeColor: '#ccc', - /** - * @cfg {Boolean} [drawNormalOnTop=false] Configure as `true` to draw the normal range overlaying the chart. - */ - drawNormalOnTop: false, - /** - * @cfg {Number} [chartRangeMin] The minimum value to use for the range of Y values of the chart - Defaults to the minimum value supplied. - */ - chartRangeMin: null, - /** - * @cfg {Number} [chartRangeMax] The maximum value to use for the range of Y values of the chart - Defaults to the minimum value supplied. - */ - chartRangeMax: null, - /** - * @cfg {Number} [chartRangeMinX] The minimum value to use for the X value of the chart. - */ - chartRangeMinX: null, - /** - * @cfg {Number} [chartRangeMaxX] The maximum value to use for the X value of the chart. - */ - chartRangeMaxX: null, - tipTpl: new Ext.XTemplate('● {prefix}{y}{suffix}'), - /** - * @cfg {Object} [valueSpots] An object which uses range specifiers as keys to indicate spot color values - * for range of values. A range specifier is of the form `[number]:[number]` indicating start and end range. - * Omitting aither means an open ended range. For example to render green spots on all values less than 50 - * and red on values higher than 50 use: - * - * { - * // Open ended range, with max value 49 - * ":49": "green", - * - * // Open ended range, with min value 50 - * "50:": "red" - * } - */ - valueSpots: null - }, - applyValueSpots: function(valueSpots) { - if (valueSpots && !valueSpots.get) { - valueSpots = new Ext.sparkline.RangeMap(valueSpots); - } - return valueSpots; - }, - onUpdate: function() { - this.vertices = []; - this.regionMap = []; - this.xvalues = []; - this.yvalues = []; - this.yminmax = []; - }, - getRegion: function(x, y) { - var i, - regionMap = this.regionMap; - // maps regions to value positions - for (i = regionMap.length; i--; ) { - if (regionMap[i] !== null && x >= regionMap[i][0] && x <= regionMap[i][1]) { - return regionMap[i][2]; - } - } - return undefined; - }, - getRegionFields: function(region) { - return { - isNull: this.yvalues[region] === null, - x: this.xvalues[region], - y: this.yvalues[region], - color: this.getLineColor(), - fillColor: this.getFillColor(), - offset: region - }; - }, - renderHighlight: function(region) { - var me = this, - canvas = me.canvas, - vertex = me.vertices[region], - spotRadius = me.getSpotRadius(), - highlightSpotColor = me.getHighlightSpotColor(), - highlightLineColor = me.getHighlightLineColor(); - if (!vertex) { - return; - } - if (spotRadius && highlightSpotColor) { - canvas.drawCircle(vertex[0], vertex[1], spotRadius, null, highlightSpotColor).append(); - } - if (highlightLineColor) { - canvas.drawLine(vertex[0], me.canvasTop, vertex[0], me.canvasTop + me.getHeight(), highlightLineColor).append(); - } - }, - scanValues: function() { - var me = this, - values = me.values, - valcount = values.length, - xvalues = me.xvalues, - yvalues = me.yvalues, - yminmax = me.yminmax, - i, val, isStr, isArray, sp; - for (i = 0; i < valcount; i++) { - val = values[i]; - isStr = typeof (values[i]) === 'string'; - isArray = typeof (values[i]) === 'object' && values[i] instanceof Array; - sp = isStr && values[i].split(':'); - if (isStr && sp.length === 2) { - // x:y - xvalues.push(Number(sp[0])); - yvalues.push(Number(sp[1])); - yminmax.push(Number(sp[1])); - } else if (isArray) { - xvalues.push(val[0]); - yvalues.push(val[1]); - yminmax.push(val[1]); - } else { - xvalues.push(i); - if (values[i] === null || values[i] === 'null') { - yvalues.push(null); - } else { - yvalues.push(Number(val)); - yminmax.push(Number(val)); - } - } - } - if (me.xvalues) { - xvalues = me.xvalues; - } - me.maxy = me.maxyorg = Math.max.apply(Math, yminmax); - me.miny = me.minyorg = Math.min.apply(Math, yminmax); - me.maxx = Math.max.apply(Math, xvalues); - me.minx = Math.min.apply(Math, xvalues); - me.xvalues = xvalues; - me.yvalues = yvalues; - me.yminmax = yminmax; - }, - processRangeOptions: function() { - var me = this, - normalRangeMin = me.getNormalRangeMin(), - normalRangeMax = me.getNormalRangeMax(), - chartRangeMin = me.getChartRangeMin(), - chartRangeMinX = me.getChartRangeMinX(), - chartRangeMax = me.getChartRangeMax(), - chartRangeMaxX = me.getChartRangeMaxX(); - if (normalRangeMin != null) { - if (normalRangeMin < me.miny) { - me.miny = normalRangeMin; - } - if (normalRangeMax > me.maxy) { - me.maxy = normalRangeMax; - } - } - if (chartRangeMin != null && (me.chartRangeClip || chartRangeMin < me.miny)) { - me.miny = chartRangeMin; - } - if (chartRangeMax != null && (me.chartRangeClip || chartRangeMax > me.maxy)) { - this.maxy = chartRangeMax; - } - if (chartRangeMinX != null && (me.chartRangeClipX || chartRangeMinX < me.minx)) { - me.minx = chartRangeMinX; - } - if (chartRangeMaxX != null && (me.chartRangeClipX || chartRangeMaxX > me.maxx)) { - me.maxx = chartRangeMaxX; - } - }, - drawNormalRange: function(canvasLeft, canvasTop, canvasHeight, canvasWidth, rangey) { - var normalRangeMin = this.getNormalRangeMin(), - normalRangeMax = this.getNormalRangeMax(), - ytop = canvasTop + Math.round(canvasHeight - (canvasHeight * ((normalRangeMax - this.miny) / rangey))), - height = Math.round((canvasHeight * (normalRangeMax - normalRangeMin)) / rangey); - this.canvas.drawRect(canvasLeft, ytop, canvasWidth, height, undefined, this.normalRangeColor).append(); - }, - renderGraph: function() { - var me = this, - canvas = me.canvas, - canvasWidth = me.getWidth(), - canvasHeight = me.getHeight(), - vertices = me.vertices, - spotRadius = me.getSpotRadius(), - regionMap = me.regionMap, - rangeX, Y, yvallast, canvasTop, canvasLeft, vertex, path, paths, x, y, xNext, xPos, xPosNext, last, next, yValCount, lineShapes, fillShapes, plen, - valueSpots = me.getValueSpots(), - hlSpotsEnabled, color, xValues, yValues, i, - spotColor = me.getSpotColor(), - minSpotColor = me.getMinSpotColor(), - maxSpotColor = me.getMaxSpotColor(), - normalRangeMin = me.getNormalRangeMin(), - drawNormalOnTop = me.getDrawNormalOnTop(); - if (!me.callParent()) { - return; - } - me.scanValues(); - me.processRangeOptions(); - xValues = me.xvalues; - yValues = me.yvalues; - if (!me.yminmax.length || me.yvalues.length < 2) { - // empty or all null valuess - return; - } - canvasTop = canvasLeft = 0; - rangeX = me.maxx - me.minx === 0 ? 1 : me.maxx - me.minx; - Y = me.maxy - me.miny === 0 ? 1 : me.maxy - me.miny; - yvallast = me.yvalues.length - 1; - if (spotRadius && (canvasWidth < (spotRadius * 4) || canvasHeight < (spotRadius * 4))) { - spotRadius = 0; - } - if (spotRadius) { - // adjust the canvas size as required so that spots will fit - hlSpotsEnabled = me.getHighlightSpotColor() && !me.disableInteraction; - if (hlSpotsEnabled || minSpotColor || (spotColor && yValues[yvallast] === me.miny)) { - canvasHeight -= Math.ceil(spotRadius); - } - if (hlSpotsEnabled || maxSpotColor || (spotColor && yValues[yvallast] === me.maxy)) { - canvasHeight -= Math.ceil(spotRadius); - canvasTop += Math.ceil(spotRadius); - } - if (hlSpotsEnabled || ((minSpotColor || maxSpotColor) && (yValues[0] === me.miny || yValues[0] === me.maxy))) { - canvasLeft += Math.ceil(spotRadius); - canvasWidth -= Math.ceil(spotRadius); - } - if (hlSpotsEnabled || spotColor || (minSpotColor || maxSpotColor && (yValues[yvallast] === me.miny || yValues[yvallast] === me.maxy))) { - canvasWidth -= Math.ceil(spotRadius); - } - } - canvasHeight--; - if (normalRangeMin != null && !drawNormalOnTop) { - me.drawNormalRange(canvasLeft, canvasTop, canvasHeight, canvasWidth, Y); - } - path = []; - paths = [ - path - ]; - last = next = null; - yValCount = yValues.length; - for (i = 0; i < yValCount; i++) { - x = xValues[i]; - xNext = xValues[i + 1]; - y = yValues[i]; - xPos = canvasLeft + Math.round((x - me.minx) * (canvasWidth / rangeX)); - xPosNext = i < yValCount - 1 ? canvasLeft + Math.round((xNext - me.minx) * (canvasWidth / rangeX)) : canvasWidth; - next = xPos + ((xPosNext - xPos) / 2); - regionMap[i] = [ - last || 0, - next, - i - ]; - last = next; - if (y === null) { - if (i) { - if (yValues[i - 1] !== null) { - path = []; - paths.push(path); - } - vertices.push(null); - } - } else { - if (y < me.miny) { - y = me.miny; - } - if (y > me.maxy) { - y = me.maxy; - } - if (!path.length) { - // previous value was null - path.push([ - xPos, - canvasTop + canvasHeight - ]); - } - vertex = [ - xPos, - canvasTop + Math.round(canvasHeight - (canvasHeight * ((y - this.miny) / Y))) - ]; - path.push(vertex); - vertices.push(vertex); - } - } - lineShapes = []; - fillShapes = []; - plen = paths.length; - for (i = 0; i < plen; i++) { - path = paths[i]; - if (path.length) { - if (me.fillColor) { - path.push([ - path[path.length - 1][0], - (canvasTop + canvasHeight) - ]); - fillShapes.push(path.slice(0)); - path.pop(); - } - // if there's only a single point in this path, then we want to display it - // as a vertical line which means we keep path[0] as is - if (path.length > 2) { - // else we want the first value - path[0] = [ - path[0][0], - path[1][1] - ]; - } - lineShapes.push(path); - } - } - // draw the fill first, then optionally the normal range, then the line on top of that - plen = fillShapes.length; - for (i = 0; i < plen; i++) { - canvas.drawShape(fillShapes[i], me.fillColor, me.fillColor).append(); - } - if (normalRangeMin != null && drawNormalOnTop) { - me.drawNormalRange(canvasLeft, canvasTop, canvasHeight, canvasWidth, Y); - } - plen = lineShapes.length; - for (i = 0; i < plen; i++) { - canvas.drawShape(lineShapes[i], me.getLineColor(), null, me.getLineWidth()).append(); - } - if (spotRadius && valueSpots) { - if (valueSpots.get == null) { - valueSpots = new Ext.sparkline.RangeMap(valueSpots); - } - for (i = 0; i < yValCount; i++) { - color = valueSpots.get(yValues[i]); - if (color) { - canvas.drawCircle(canvasLeft + Math.round((xValues[i] - me.minx) * (canvasWidth / rangeX)), canvasTop + Math.round(canvasHeight - (canvasHeight * ((yValues[i] - me.miny) / Y))), spotRadius, null, color).append(); - } - } - } - if (spotRadius && spotColor && yValues[yvallast] != null) { - canvas.drawCircle(canvasLeft + Math.round((xValues[xValues.length - 1] - me.minx) * (canvasWidth / rangeX)), canvasTop + Math.round(canvasHeight - (canvasHeight * ((yValues[yvallast] - me.miny) / Y))), spotRadius, null, spotColor).append(); - } - if (me.maxy !== me.minyorg) { - if (spotRadius && minSpotColor) { - x = xValues[Ext.Array.indexOf(yValues, me.minyorg)]; - canvas.drawCircle(canvasLeft + Math.round((x - me.minx) * (canvasWidth / rangeX)), canvasTop + Math.round(canvasHeight - (canvasHeight * ((me.minyorg - me.miny) / Y))), spotRadius, null, minSpotColor).append(); - } - if (spotRadius && maxSpotColor) { - x = xValues[Ext.Array.indexOf(yValues, me.maxyorg)]; - canvas.drawCircle(canvasLeft + Math.round((x - me.minx) * (canvasWidth / rangeX)), canvasTop + Math.round(canvasHeight - (canvasHeight * ((me.maxyorg - me.miny) / Y))), spotRadius, null, maxSpotColor).append(); - } - } - me.canvasTop = canvasTop; - // If mouse is over, apply the highlight - if (me.currentPageXY && me.el.getRegion().contains(me.currentPageXY)) { - me.updateDisplay(); - } - canvas.render(); - } -}); - -/** - * @class Ext.sparkline.Pie - * - * Plots a pie chart based upon the input {#values} array. - * - * See {@link Ext.sparkline.Base the base class} for a simple example. - */ -Ext.define('Ext.sparkline.Pie', { - extend: 'Ext.sparkline.Base', - alias: 'widget.sparklinepie', - config: { - /** - * @cfg {Number} [offset] Angle in degrees to offset the first slice. - */ - offset: 0, - /** - * @cfg {String[]} [sliceColors] An array of CSS colro values to apply to the chart slices. - */ - sliceColors: [ - '#3366cc', - '#dc3912', - '#ff9900', - '#109618', - '#66aa00', - '#dd4477', - '#0099c6', - '#990099' - ], - /** - * @cfg {Number} [borderWidth=0] Border width in pixels of line round slices. - */ - borderWidth: 0, - /** - * @cfg {String} [borderColor=#000] Border color of line round slices. - */ - borderColor: '#000', - tipTpl: new Ext.XTemplate('● {value} ({percent:number("0.0")}%)') - }, - // Ensure values is an array of numbers - applyValues: function(newValues) { - newValues = Ext.Array.map(Ext.Array.from(newValues), Number); - this.disabled = !(newValues && newValues.length); - return newValues; - }, - onUpdate: function() { - var me = this, - values = me.values, - total = 0, - i; - me.callParent(arguments); - me.shapes = {}; - // map shape ids to value offsets - me.valueShapes = {}; - // maps value offsets to shape ids - if (values.length > 0) { - for (i = values.length; i--; ) { - total += values[i]; - } - } - me.total = total; - me.radius = Math.floor(Math.min(me.getWidth(), me.getHeight()) / 2); - }, - getRegion: function(x, y) { - var ratio = window.devicePixelRatio || 1, - shapeid = this.canvas.getShapeAt(x * ratio, y * ratio); - return (shapeid != null && this.shapes[shapeid] != null) ? this.shapes[shapeid] : null; - }, - getRegionFields: function(region) { - var sliceColors = this.getSliceColors(); - return { - isNull: this.values[region] == null, - value: this.values[region], - percent: this.values[region] / this.total * 100, - color: sliceColors[region % sliceColors.length], - offset: region - }; - }, - renderHighlight: function(region) { - this.renderSlice(region, true).append(); - }, - renderSlice: function(valuenum, highlight) { - var me = this, - canvas = me.canvas, - radius = me.radius, - borderWidth = me.getBorderWidth(), - offset = me.getOffset(), - circle = 2 * Math.PI, - values = me.values, - total = me.total, - next = offset ? (2 * Math.PI) * (offset / 360) : 0, - start, end, i, vlen, color, - sliceColors = this.getSliceColors(); - vlen = values.length; - for (i = 0; i < vlen; i++) { - start = next; - end = next; - if (total > 0) { - // avoid divide by zero - end = next + (circle * (values[i] / total)); - } - if (valuenum === i) { - color = sliceColors[i % sliceColors.length]; - if (highlight) { - color = me.calcHighlightColor(color); - } - return canvas.drawPieSlice(radius, radius, radius - borderWidth, start, end, null, color); - } - next = end; - } - }, - renderGraph: function() { - var me = this, - canvas = me.canvas, - values = me.values, - radius = me.radius, - borderWidth = me.getBorderWidth(), - shape, i, - shapes = me.shapes || (me.shapes = {}), - valueShapes = me.valueShapes || (me.valueShapes = {}); - if (!me.callParent()) { - return; - } - if (borderWidth) { - canvas.drawCircle(radius, radius, Math.floor(radius - (borderWidth / 2)), me.getBorderColor(), null, borderWidth).append(); - } - for (i = values.length; i--; ) { - if (values[i]) { - // don't render zero values - shape = me.renderSlice(i).append(); - valueShapes[i] = shape.id; - // store just the shapeid - shapes[shape.id] = i; - } - } - // If mouse is over, re-apply the highlight - if (me.currentPageXY && me.el.getRegion().contains(me.currentPageXY)) { - me.currentRegion = null; - me.updateDisplay(); - } - canvas.render(); - } -}); - -/** - * @class Ext.sparkline.TriState - * - * Plots bars based upon "win"/"draw" or "lose" status of the input {@link #values} array. Positive values mean - * a win, zero a draw, and negative a lose. - * - * See {@link Ext.sparkline.Base the base class} for a simple example. - */ -Ext.define('Ext.sparkline.TriState', { - extend: 'Ext.sparkline.BarBase', - requires: [ - 'Ext.sparkline.RangeMap' - ], - alias: 'widget.sparklinetristate', - config: { - /** - * @cfg {Number} [barWidth=4] The pixel width of each bar. - */ - barWidth: 4, - /** - * @cfg {Number} [barSpacing=1] The pixel spacing between each bar. - */ - barSpacing: 1, - /** - * @cfg {String} [posBarColor=#6f6] The color for positive value bars. - */ - posBarColor: '#6f6', - /** - * @cfg {String} [negBarColor=#f44] The color for negative value bars. - */ - negBarColor: '#f44', - /** - * @cfg {String} [zeroBarColor=#999] The color for zero value bars. - */ - zeroBarColor: '#999', - /** - * @cfg {Object} [colorMap] An object that uses range specifiers as keys to - * indicate bar color values for a range of values. A range specifier is - * specified in the form `[number]:[number]`, which indicates start and end range. - * Omitting either means an open ended range. - * - * For example, to render green bars on all values less than -1 and red on values - * higher than 1, you would use: - * - * colorMap: { - * // Open ended range, with max value -1 - * ":-1": "green", - * - * // Open ended range, with min value 1 - * "1:": "red" - * } - */ - colorMap: {}, - tipTpl: new Ext.XTemplate('● {value:this.states}', { - states: function(v) { - var value = Number(v); - if (value === -1) { - return 'Loss'; - } - if (value === 0) { - return 'Draw'; - } - if (value === 1) { - return 'Win'; - } - return v; - } - }) - }, - applyColorMap: function(colorMap) { - var me = this; - if (Ext.isArray(colorMap)) { - me.colorMapByIndex = colorMap; - me.colorMapByValue = null; - } else { - me.colorMapByIndex = null; - me.colorMapByValue = colorMap; - if (me.colorMapByValue && me.colorMapByValue.get == null) { - me.colorMapByValue = new Ext.sparkline.RangeMap(colorMap); - } - } - return colorMap; - }, - // Ensure values is an array of numbers - applyValues: function(newValues) { - newValues = Ext.Array.map(Ext.Array.from(newValues), Number); - this.disabled = !(newValues && newValues.length); - return newValues; - }, - onUpdate: function() { - this.totalBarWidth = this.getBarWidth() + this.getBarSpacing(); - }, - getBarWidth: function() { - var values = this.values; - return this._barWidth || (this.getWidth() - (values.length - 1) * this.getBarSpacing()) / values.length; - }, - getRegion: function(x, y) { - return Math.floor(x / this.totalBarWidth); - }, - getRegionFields: function(region) { - return { - isNull: this.values[region] == null, - value: this.values[region], - color: this.calcColor(this.values[region], region), - offset: region - }; - }, - calcColor: function(value, valuenum) { - var me = this, - values = me.values, - colorMapByIndex = me.colorMapByIndex, - colorMapByValue = me.colorMapByValue, - color, newColor; - if (colorMapByValue && (newColor = colorMapByValue.get(value))) { - color = newColor; - } else if (colorMapByIndex && colorMapByIndex.length > valuenum) { - color = colorMapByIndex[valuenum]; - } else if (values[valuenum] < 0) { - color = me.getNegBarColor(); - } else if (values[valuenum] > 0) { - color = me.getPosBarColor(); - } else { - color = me.getZeroBarColor(); - } - return color; - }, - renderRegion: function(valuenum, highlight) { - var me = this, - values = me.values, - canvas = me.canvas, - canvasHeight, height, halfHeight, x, y, color; - canvasHeight = canvas.pixelHeight; - halfHeight = Math.round(canvasHeight / 2); - x = valuenum * me.totalBarWidth; - if (values[valuenum] < 0) { - y = halfHeight; - height = halfHeight - 1; - } else if (values[valuenum] > 0) { - y = 0; - height = halfHeight - 1; - } else { - y = halfHeight - 1; - height = 2; - } - color = me.calcColor(values[valuenum], valuenum); - if (color == null) { - return; - } - if (highlight) { - color = me.calcHighlightColor(color); - } - canvas.drawRect(x, y, me.getBarWidth() - 1, height - 1, color, color).append(); - } -}); - -/** - * A Provider implementation which saves and retrieves state via cookies. The CookieProvider supports the usual cookie - * options, such as: - * - * - {@link #path} - * - {@link #expires} - * - {@link #domain} - * - {@link #secure} - * - * Example: - * - * var cp = Ext.create('Ext.state.CookieProvider', { - * path: "/cgi-bin/", - * expires: new Date(new Date().getTime()+(1000*60*60*24*30)), //30 days - * domain: "sencha.com" - * }); - * - * Ext.state.Manager.setProvider(cp); - * - */ -Ext.define('Ext.state.CookieProvider', { - extend: 'Ext.state.Provider', - /** - * @cfg {String} path - * The path for which the cookie is active. Defaults to root '/' which makes it active for all pages in the site. - */ - /** - * @cfg {Date} expires - * The cookie expiration date. Defaults to 7 days from now. - */ - /** - * @cfg {String} domain - * The domain to save the cookie for. Note that you cannot specify a different domain than your page is on, but you can - * specify a sub-domain, or simply the domain itself like 'sencha.com' to include all sub-domains if you need to access - * cookies across different sub-domains. Defaults to null which uses the same domain the page is running on including - * the 'www' like 'www.sencha.com'. - */ - /** - * @cfg {Boolean} [secure=false] - * True if the site is using SSL - */ - /** - * Creates a new CookieProvider. - * @param {Object} [config] Config object. - */ - constructor: function(config) { - var me = this; - me.path = "/"; - me.expires = new Date(Ext.Date.now() + (1000 * 60 * 60 * 24 * 7)); - //7 days - me.domain = null; - me.secure = false; - me.callParent(arguments); - me.state = me.readCookies(); - }, - /** - * @private - */ - set: function(name, value) { - var me = this; - if (typeof value === "undefined" || value === null) { - me.clear(name); - return; - } - me.setCookie(name, value); - me.callParent(arguments); - }, - /** - * @private - */ - clear: function(name) { - this.clearCookie(name); - this.callParent(arguments); - }, - /** - * @private - */ - readCookies: function() { - var cookies = {}, - c = document.cookie + ";", - re = /\s?(.*?)=(.*?);/g, - prefix = this.prefix, - len = prefix.length, - matches, name, value; - while ((matches = re.exec(c)) != null) { - name = matches[1]; - value = matches[2]; - if (name && name.substring(0, len) === prefix) { - cookies[name.substr(len)] = this.decodeValue(value); - } - } - return cookies; - }, - /** - * @private - */ - setCookie: function(name, value) { - var me = this; - document.cookie = me.prefix + name + "=" + me.encodeValue(value) + ((me.expires == null) ? "" : ("; expires=" + me.expires.toUTCString())) + ((me.path == null) ? "" : ("; path=" + me.path)) + ((me.domain == null) ? "" : ("; domain=" + me.domain)) + (me.secure ? "; secure" : ""); - }, - /** - * @private - */ - clearCookie: function(name) { - var me = this; - document.cookie = me.prefix + name + "=null; expires=Thu, 01-Jan-1970 00:00:01 GMT" + ((me.path == null) ? "" : ("; path=" + me.path)) + ((me.domain == null) ? "" : ("; domain=" + me.domain)) + (me.secure ? "; secure" : ""); - } -}); - -/** - * A Provider implementation which saves and retrieves state via the HTML5 localStorage API - * or IE `userData` storage. For details see `Ext.util.LocalStorage`. - * - * If the browser does not support local storage, there will be no attempt to read the state. - * Before creating this class, check {@link Ext.util.LocalStorage#supported}. - */ -Ext.define('Ext.state.LocalStorageProvider', { - extend: 'Ext.state.Provider', - requires: [ - 'Ext.util.LocalStorage' - ], - alias: 'state.localstorage', - constructor: function() { - var me = this; - me.callParent(arguments); - me.store = me.getStorageObject(); - if (me.store) { - me.state = me.readLocalStorage(); - } else { - me.state = {}; - } - }, - readLocalStorage: function() { - var store = this.store, - data = {}, - keys = store.getKeys(), - i = keys.length, - key; - while (i--) { - key = keys[i]; - data[key] = this.decodeValue(store.getItem(key)); - } - return data; - }, - set: function(name, value) { - var me = this; - me.clear(name); - if (value != null) { - // !== undefined && !== null - me.store.setItem(name, me.encodeValue(value)); - me.callParent(arguments); - } - }, - /** - * @private - */ - clear: function(name) { - this.store.removeItem(name); - this.callParent(arguments); - }, - getStorageObject: function() { - var prefix = this.prefix, - id = prefix, - n = id.length - 1; - if (id.charAt(n) === '-') { - id = id.substring(0, n); - } - return new Ext.util.LocalStorage({ - id: id, - prefix: prefix - }); - } -}); - -/** - * Represents a single Tab in a {@link Ext.tab.Panel TabPanel}. A Tab is simply a slightly customized {@link Ext.button.Button Button}, - * styled to look like a tab. Tabs are optionally closable, and can also be disabled. 99% of the time you will not - * need to create Tabs manually as the framework does so automatically when you use a {@link Ext.tab.Panel TabPanel} - */ -Ext.define('Ext.tab.Tab', { - extend: 'Ext.button.Button', - alias: 'widget.tab', - /** - * @property {Boolean} isTab - * `true` in this class to identify an object as an instantiated Tab, or subclass thereof. - */ - isTab: true, - baseCls: Ext.baseCSSPrefix + 'tab', - closeElOverCls: Ext.baseCSSPrefix + 'tab-close-btn-over', - closeElPressedCls: Ext.baseCSSPrefix + 'tab-close-btn-pressed', - config: { - /** - * @cfg {'default'/0/1/2} rotation - * The rotation of the tab. Can be one of the following values: - * - * - `null` - use the default rotation, depending on the dock position of the tabbar - * - `0` - no rotation - * - `1` - rotate 90deg clockwise - * - `2` - rotate 90deg counter-clockwise - * - * The default behavior of this config depends on the dock position of the tabbar: - * - * - `'top'` or `'bottom'` - `0` - * - `'right'` - `1` - * - `'left'` - `2` - */ - rotation: 'default', - /** - * @cfg {'top'/'right'/'bottom'/'left'} tabPosition - * The tab's position. Users should not typically need to set this, as it is - * configured automatically by the tab bar - */ - tabPosition: 'top' - }, - /** - * @cfg {Boolean} closable - * True to make the Tab start closable (the close icon will be visible). - */ - closable: true, - // - /** - * @cfg {String} closeText - * The accessible text label for the close button link to be announced by screen readers - * when the tab is focused. Only used when {@link #cfg-closable} = true. - */ - closeText: 'Close Tab', - // - /** - * @property {Boolean} active - * Indicates that this tab is currently active. This is NOT a public configuration. - * @readonly - */ - active: false, - /** - * @property {Boolean} closable - * True if the tab is currently closable - */ - childEls: [ - 'closeEl' - ], - scale: false, - /** - * @event activate - * Fired when the tab is activated. - * @param {Ext.tab.Tab} this - */ - /** - * @event deactivate - * Fired when the tab is deactivated. - * @param {Ext.tab.Tab} this - */ - /** - * @event beforeclose - * Fires if the user clicks on the Tab's close button, but before the {@link #close} event is fired. Return - * false from any listener to stop the close event being fired - * @param {Ext.tab.Tab} tab The Tab object - */ - /** - * @event close - * Fires to indicate that the tab is to be closed, usually because the user has clicked the close button. - * @param {Ext.tab.Tab} tab The Tab object - */ - ariaRole: 'tab', - tabIndex: -1, - keyHandlers: { - DELETE: 'onDeleteKey' - }, - _btnWrapCls: Ext.baseCSSPrefix + 'tab-wrap', - _btnCls: Ext.baseCSSPrefix + 'tab-button', - _baseIconCls: Ext.baseCSSPrefix + 'tab-icon-el', - _glyphCls: Ext.baseCSSPrefix + 'tab-glyph', - _innerCls: Ext.baseCSSPrefix + 'tab-inner', - _textCls: Ext.baseCSSPrefix + 'tab-text', - _noTextCls: Ext.baseCSSPrefix + 'tab-no-text', - _hasIconCls: Ext.baseCSSPrefix + 'tab-icon', - _activeCls: Ext.baseCSSPrefix + 'tab-active', - _closableCls: Ext.baseCSSPrefix + 'tab-closable', - overCls: Ext.baseCSSPrefix + 'tab-over', - _pressedCls: Ext.baseCSSPrefix + 'tab-pressed', - _disabledCls: Ext.baseCSSPrefix + 'tab-disabled', - _rotateClasses: { - 1: Ext.baseCSSPrefix + 'tab-rotate-right', - 2: Ext.baseCSSPrefix + 'tab-rotate-left' - }, - // a mapping of the "ui" positions. When "rotation" is anything other than 0, a ui - // position other than the docked side must be used. - _positions: { - top: { - 'default': 'top', - 0: 'top', - 1: 'left', - 2: 'right' - }, - right: { - 'default': 'top', - 0: 'right', - 1: 'top', - 2: 'bottom' - }, - bottom: { - 'default': 'bottom', - 0: 'bottom', - 1: 'right', - 2: 'left' - }, - left: { - 'default': 'top', - 0: 'left', - 1: 'bottom', - 2: 'top' - } - }, - _defaultRotations: { - top: 0, - right: 1, - bottom: 0, - left: 2 - }, - initComponent: function() { - var me = this; - if (me.card) { - me.setCard(me.card); - } - me.callParent(arguments); - }, - getActualRotation: function() { - var rotation = this.getRotation(); - return (rotation !== 'default') ? rotation : this._defaultRotations[this.getTabPosition()]; - }, - updateRotation: function() { - this.syncRotationAndPosition(); - }, - updateTabPosition: function() { - this.syncRotationAndPosition(); - }, - syncRotationAndPosition: function() { - var me = this, - rotateClasses = me._rotateClasses, - position = me.getTabPosition(), - rotation = me.getActualRotation(), - oldRotateCls = me._rotateCls, - rotateCls = me._rotateCls = rotateClasses[rotation], - oldPositionCls = me._positionCls, - positionCls = me._positionCls = me._positions[position][rotation]; - if (oldRotateCls !== rotateCls) { - if (oldRotateCls) { - me.removeCls(oldRotateCls); - } - if (rotateCls) { - me.addCls(rotateCls); - } - } - if (oldPositionCls !== positionCls) { - if (oldPositionCls) { - me.removeClsWithUI(oldPositionCls); - } - if (positionCls) { - me.addClsWithUI(positionCls); - } - if (me.rendered) { - me.updateFrame(); - } - } - if (me.rendered) { - me.setElOrientation(); - } - }, - onAdded: function(container, pos, instanced) { - this.callParent([ - container, - pos, - instanced - ]); - this.syncRotationAndPosition(); - }, - getTemplateArgs: function() { - var me = this, - result = me.callParent(); - result.closable = me.closable; - result.closeText = me.closeText; - return result; - }, - beforeRender: function() { - var me = this, - tabBar = me.up('tabbar'), - tabPanel = me.up('tabpanel'); - me.callParent(); - me.ariaRenderAttributes = me.ariaRenderAttributes || {}; - if (me.active) { - me.ariaRenderAttributes['aria-selected'] = true; - me.addCls(me._activeCls); - } else { - me.ariaRenderAttributes['aria-selected'] = false; - } - me.syncClosableCls(); - // Propagate minTabWidth and maxTabWidth settings from the owning TabBar then TabPanel - if (!me.minWidth) { - me.minWidth = (tabBar) ? tabBar.minTabWidth : me.minWidth; - if (!me.minWidth && tabPanel) { - me.minWidth = tabPanel.minTabWidth; - } - if (me.minWidth && me.iconCls) { - me.minWidth += 25; - } - } - if (!me.maxWidth) { - me.maxWidth = (tabBar) ? tabBar.maxTabWidth : me.maxWidth; - if (!me.maxWidth && tabPanel) { - me.maxWidth = tabPanel.maxTabWidth; - } - } - }, - onRender: function() { - var me = this; - me.setElOrientation(); - me.callParent(arguments); - if (me.closable) { - me.closeEl.addClsOnOver(me.closeElOverCls); - me.closeEl.addClsOnClick(me.closeElPressedCls); - } - }, - setElOrientation: function() { - var me = this, - rotation = me.getActualRotation(), - el = me.el; - if (rotation) { - el.setVertical(rotation === 1 ? 90 : 270); - } else { - el.setHorizontal(); - } - }, - enable: function(silent) { - var me = this; - me.callParent(arguments); - me.removeCls(me._disabledCls); - return me; - }, - disable: function(silent) { - var me = this; - me.callParent(arguments); - me.addCls(me._disabledCls); - return me; - }, - /** - * Sets the tab as either closable or not. - * @param {Boolean} closable Pass false to make the tab not closable. Otherwise the tab will be made closable (eg a - * close button will appear on the tab) - */ - setClosable: function(closable) { - var me = this; - // Closable must be true if no args - closable = (!arguments.length || !!closable); - if (me.closable !== closable) { - me.closable = closable; - // set property on the user-facing item ('card'): - if (me.card) { - me.card.closable = closable; - } - me.syncClosableCls(); - if (me.rendered) { - me.syncClosableElements(); - // Tab will change width to accommodate close icon - me.updateLayout(); - } - } - }, - /** - * This method ensures that the closeBtn element exists or not based on 'closable'. - * @private - */ - syncClosableElements: function() { - var me = this, - closeEl = me.closeEl; - if (me.closable) { - if (!closeEl) { - closeEl = me.closeEl = me.btnWrap.insertSibling({ - tag: 'span', - id: me.id + '-closeEl', - cls: me.baseCls + '-close-btn', - html: me.closeText - }, 'after'); - } - closeEl.addClsOnOver(me.closeElOverCls); - closeEl.addClsOnClick(me.closeElPressedCls); - } else if (closeEl) { - closeEl.destroy(); - delete me.closeEl; - } - }, - /** - * This method ensures that the closable cls are added or removed based on 'closable'. - * @private - */ - syncClosableCls: function() { - var me = this, - closableCls = me._closableCls; - if (me.closable) { - me.addCls(closableCls); - } else { - me.removeCls(closableCls); - } - }, - /** - * Sets this tab's attached card. Usually this is handled automatically by the {@link Ext.tab.Panel} that this Tab - * belongs to and would not need to be done by the developer - * @param {Ext.Component} card The card to set - */ - setCard: function(card) { - var me = this; - me.card = card; - if (card.iconAlign) { - me.setIconAlign(card.iconAlign); - } - if (card.textAlign) { - me.setTextAlign(card.textAlign); - } - me.setText(me.title || card.title); - me.setIconCls(me.iconCls || card.iconCls); - me.setIcon(me.icon || card.icon); - me.setGlyph(me.glyph || card.glyph); - }, - /** - * @private - * Listener attached to click events on the Tab's close button - */ - onCloseClick: function() { - var me = this; - if (me.fireEvent('beforeclose', me) !== false) { - if (me.tabBar) { - if (me.tabBar.closeTab(me) === false) { - // beforeclose on the panel vetoed the event, stop here - return; - } - } else { - // if there's no tabbar, fire the close event - me.fireClose(); - } - } - }, - /** - * Fires the close event on the tab. - * @private - */ - fireClose: function() { - this.fireEvent('close', this); - }, - /** - * @private - */ - onEnterKey: function(e) { - var me = this; - if (me.tabBar) { - me.tabBar.onClick(e, me.el); - e.stopEvent(); - return false; - } - }, - /** - * @private - */ - onDeleteKey: function(e) { - if (this.closable) { - this.onCloseClick(); - e.stopEvent(); - return false; - } - }, - /** - * @private - */ - beforeClick: function(isCloseClick) { - if (!isCloseClick) { - this.focus(); - } - }, - /** - * @private - */ - activate: function(supressEvent) { - var me = this, - card = me.card, - ariaDom = me.ariaEl.dom; - me.active = true; - me.addCls(me._activeCls); - if (ariaDom) { - ariaDom.setAttribute('aria-selected', true); - } else { - me.ariaRenderAttributes = me.ariaRenderAttributes || {}; - me.ariaRenderAttributes['aria-selected'] = true; - } - if (card) { - if (card.ariaEl.dom) { - card.ariaEl.dom.setAttribute('aria-expanded', true); - } else { - card.ariaRenderAttributes = card.ariaRenderAttributes || {}; - card.ariaRenderAttributes['aria-expanded'] = true; - } - } - if (supressEvent !== true) { - me.fireEvent('activate', me); - } - }, - /** - * @private - */ - deactivate: function(supressEvent) { - var me = this, - card = me.card, - ariaDom = me.ariaEl.dom; - me.active = false; - me.removeCls(me._activeCls); - if (ariaDom) { - ariaDom.setAttribute('aria-selected', false); - } else { - me.ariaRenderAttributes = me.ariaRenderAttributes || {}; - me.ariaRenderAttributes['aria-selected'] = false; - } - if (card) { - if (card.ariaEl.dom) { - card.ariaEl.dom.setAttribute('aria-expanded', false); - } else { - card.ariaRenderAttributes = card.ariaRenderAttributes || {}; - card.ariaRenderAttributes['aria-expanded'] = false; - } - } - if (supressEvent !== true) { - me.fireEvent('deactivate', me); - } - }, - privates: { - getFramingInfoCls: function() { - return this.baseCls + '-' + this.ui + '-' + this._positionCls; - }, - wrapPrimaryEl: function(dom) { - // Tabs don't need the hacks in Ext.dom.ButtonElement - Ext.Button.superclass.wrapPrimaryEl.call(this, dom); - } - } -}); - -/** - * TabBar is used internally by a {@link Ext.tab.Panel TabPanel} and typically should not - * need to be created manually. - */ -Ext.define('Ext.tab.Bar', { - extend: 'Ext.panel.Bar', - xtype: 'tabbar', - baseCls: Ext.baseCSSPrefix + 'tab-bar', - requires: [ - 'Ext.tab.Tab', - 'Ext.util.Point', - 'Ext.layout.component.Body' - ], - mixins: [ - 'Ext.util.FocusableContainer' - ], - componentLayout: 'body', - /** - * @property {Boolean} isTabBar - * `true` in this class to identify an object as an instantiated Tab Bar, or subclass thereof. - */ - isTabBar: true, - config: { - /** - * @cfg {'default'/0/1/2} tabRotation - * The rotation of the tabs. Can be one of the following values: - * - * - `default` - use the default rotation, depending on the dock position (see below) - * - `0` - no rotation - * - `1` - rotate 90deg clockwise - * - `2` - rotate 90deg counter-clockwise - * - * The default behavior of this config depends on the dock position: - * - * - `'top'` or `'bottom'` - `0` - * - `'right'` - `1` - * - `'left'` - `2` - */ - tabRotation: 'default', - /** - * @cfg {Boolean} tabStretchMax - * `true` to stretch all tabs to the height of the tallest tab when the tabBar - * is docked horizontally, or the width of the widest tab when the tabBar is - * docked vertically. - */ - tabStretchMax: true, - // NB: This option is named this way for the intent, but in fact activation - // happens in arrow key handler, not in focus handler. In IE focus events are - // asynchronous, so activation happens before the tab's focus handler is fired. - /** - * @cfg {Boolean} [activateOnFocus=true] - * `true` to follow WAI-ARIA requirement and activate tab when it is navigated to - * with arrow keys, or `false` to disable that behavior. When activation on focus - * is disabled, users will have to use arrow keys to focus a tab, and then press - * Space key to activate it. - */ - activateOnFocus: true - }, - /** - * @private - */ - defaultType: 'tab', - /** - * @cfg {Boolean} plain - * True to not show the full background on the tabbar - */ - plain: false, - /** - * @cfg {Boolean} ensureActiveVisibleOnChange - * `true` to ensure the active tab is scrolled into view when the tab changes, the text, the - * icon or the glyph. This is only applicable if using an overflow scroller. - * - * @since 5.1.1 - */ - ensureActiveVisibleOnChange: true, - ariaRole: 'tablist', - childEls: [ - 'body', - 'strip' - ], - _stripCls: Ext.baseCSSPrefix + 'tab-bar-strip', - _baseBodyCls: Ext.baseCSSPrefix + 'tab-bar-body', - /** - * @private - */ - renderTpl: '' + '', - /** - * @cfg {Number} minTabWidth - * The minimum width for a tab in this tab Bar. Defaults to the tab Panel's {@link Ext.tab.Panel#minTabWidth minTabWidth} value. - * @deprecated This config is deprecated. It is much easier to use the {@link Ext.tab.Panel#minTabWidth minTabWidth} config on the TabPanel. - */ - /** - * @cfg {Number} maxTabWidth - * The maximum width for a tab in this tab Bar. Defaults to the tab Panel's {@link Ext.tab.Panel#maxTabWidth maxTabWidth} value. - * @deprecated This config is deprecated. It is much easier to use the {@link Ext.tab.Panel#maxTabWidth maxTabWidth} config on the TabPanel. - */ - _reverseDockNames: { - left: 'right', - right: 'left' - }, - _layoutAlign: { - top: 'end', - right: 'begin', - bottom: 'begin', - left: 'end' - }, - /** - * @event change - * Fired when the currently-active tab has changed - * @param {Ext.tab.Bar} tabBar The TabBar - * @param {Ext.tab.Tab} tab The new Tab - * @param {Ext.Component} card The card that was just shown in the TabPanel - */ - /** - * @private - */ - initComponent: function() { - var me = this, - initialLayout = me.initialConfig.layout, - initialAlign = initialLayout && initialLayout.align, - initialOverflowHandler = initialLayout && initialLayout.overflowHandler; - if (me.plain) { - me.addCls(me.baseCls + '-plain'); - } - me.callParent(); - me.setLayout({ - align: initialAlign || (me.getTabStretchMax() ? 'stretchmax' : me._layoutAlign[me.dock]), - overflowHandler: initialOverflowHandler || 'scroller' - }); - me.on({ - click: me.onClick, - element: 'el', - scope: me - }); - }, - /** - * Ensure the passed tab is visible if using overflow scrolling - * @param {Ext.tab.Tab/Ext.Component/Number} [tab] The tab, item in the owning {@link Ext.tab.Panel} or - * the index of the item to scroll to. Defaults to the active tab. - */ - ensureTabVisible: function(tab) { - var me = this, - tabPanel = me.tabPanel, - overflowHandler = me.layout.overflowHandler; - if (me.rendered && overflowHandler && me.tooNarrow && overflowHandler.scrollToItem) { - if (tab || tab === 0) { - if (!tab.isTab) { - if (Ext.isNumber(tab)) { - tab = this.items.getAt(tab); - } else if (tab.isComponent && tabPanel && tabPanel.items.contains(tab)) { - tab = tab.tab; - } - } - } - if (!tab) { - tab = me.activeTab; - } - if (tab) { - overflowHandler.scrollToItem(tab); - } - } - }, - initRenderData: function() { - var me = this; - return Ext.apply(me.callParent(), { - bodyCls: me.bodyCls, - baseBodyCls: me._baseBodyCls, - bodyTargetCls: me.bodyTargetCls, - stripCls: me._stripCls, - dock: me.dock - }); - }, - setDock: function(dock) { - var me = this, - items = me.items, - ownerCt = me.ownerCt, - item, i, ln; - items = items && items.items; - if (items) { - for (i = 0 , ln = items.length; i < ln; i++) { - item = items[i]; - if (item.isTab) { - item.setTabPosition(dock); - } - } - } - if (me.rendered) { - // TODO: remove resetItemMargins once EXTJS-13359 is fixed - me.resetItemMargins(); - if (ownerCt && ownerCt.isHeader) { - ownerCt.resetItemMargins(); - } - me.needsScroll = true; - } - me.callParent([ - dock - ]); - }, - updateTabRotation: function(tabRotation) { - var me = this, - items = me.items, - i, ln, item; - items = items && items.items; - if (items) { - for (i = 0 , ln = items.length; i < ln; i++) { - item = items[i]; - if (item.isTab) { - item.setRotation(tabRotation); - } - } - } - if (me.rendered) { - // TODO: remove resetItemMargins once EXTJS-13359 is fixed - me.resetItemMargins(); - me.needsScroll = true; - me.updateLayout(); - } - }, - onRender: function() { - var me = this; - me.callParent(); - if (Ext.isIE8 && me.vertical) { - me.el.on({ - mousemove: me.onMouseMove, - scope: me - }); - } - }, - afterLayout: function() { - this.adjustTabPositions(); - this.callParent(arguments); - }, - onAdd: function(tab, pos) { - var fn = this.onTabContentChange; - if (this.ensureActiveVisibleOnChange) { - tab.barListeners = tab.on({ - scope: this, - destroyable: true, - glyphchange: fn, - iconchange: fn, - textchange: fn - }); - } - this.callParent([ - tab, - pos - ]); - }, - onAdded: function(container, pos, instanced) { - if (container.isHeader) { - this.addCls(container.baseCls + '-' + container.ui + '-tab-bar'); - } - this.callParent([ - container, - pos, - instanced - ]); - }, - onRemove: function(tab, destroying) { - var me = this; - // If we're not destroying, no need to do this here since they will - // be cleaned up - if (me.ensureActiveVisibleOnChange) { - if (!destroying) { - tab.barListeners.destroy(); - } - tab.barListeners = null; - } - if (tab === me.previousTab) { - me.previousTab = null; - } - me.callParent([ - tab, - destroying - ]); - }, - onRemoved: function(destroying) { - var ownerCt = this.ownerCt; - if (ownerCt.isHeader) { - this.removeCls(ownerCt.baseCls + '-' + ownerCt.ui + '-tab-bar'); - } - this.callParent([ - destroying - ]); - }, - onTabContentChange: function(tab) { - if (tab === this.activeTab) { - this.ensureTabVisible(tab); - } - }, - afterComponentLayout: function(width) { - var me = this, - needsScroll = me.needsScroll, - overflowHandler = me.layout.overflowHandler; - me.callParent(arguments); - if (overflowHandler && needsScroll && me.tooNarrow && overflowHandler.scrollToItem) { - overflowHandler.scrollToItem(me.activeTab); - } - delete me.needsScroll; - }, - /** - * @private - */ - onMouseMove: function(e) { - var me = this, - overTab = me._overTab, - tabInfo, tab; - if (e.getTarget('.' + Ext.baseCSSPrefix + 'box-scroller')) { - return; - } - tabInfo = me.getTabInfoFromPoint(e.getXY()); - tab = tabInfo.tab; - if (tab !== overTab) { - if (overTab && overTab.rendered) { - overTab.onMouseLeave(e); - me._overTab = null; - } - if (tab) { - tab.onMouseEnter(e); - me._overTab = tab; - if (!tab.disabled) { - me.el.setStyle('cursor', 'pointer'); - } - } else { - me.el.setStyle('cursor', 'default'); - } - } - }, - onMouseLeave: function(e) { - var overTab = this._overTab; - if (overTab && overTab.rendered) { - overTab.onMouseLeave(e); - } - }, - /** - * @private - * in IE8 and IE9 the clickable region of a rotated element is not its new rotated - * position, but it's original unrotated position. The result is that rotated tabs do - * not capture click and mousenter/mosueleave events correctly. This method accepts - * an xy position and calculates if the coordinates are within a tab and if they - * are within the tab's close icon (if any) - */ - getTabInfoFromPoint: function(xy) { - var me = this, - tabs = me.items.items, - length = tabs.length, - innerCt = me.layout.innerCt, - innerCtXY = innerCt.getXY(), - point = new Ext.util.Point(xy[0], xy[1]), - i = 0, - lastBox, tabRegion, closeEl, close, closeXY, closeX, closeY, closeWidth, closeHeight, tabX, tabY, tabWidth, tabHeight, closeRegion, isTabReversed, direction, tab; - for (; i < length; i++) { - tab = tabs[i]; - lastBox = tab.lastBox; - if (!lastBox || !tab.isTab) { - // avoid looping hidden or not laid out, or if the item - // is not a tab - - continue; - } - tabX = innerCtXY[0] + lastBox.x; - tabY = innerCtXY[1] - innerCt.dom.scrollTop + lastBox.y; - tabWidth = lastBox.width; - tabHeight = lastBox.height; - tabRegion = new Ext.util.Region(tabY, tabX + tabWidth, tabY + tabHeight, tabX); - if (tabRegion.contains(point)) { - closeEl = tab.closeEl; - if (closeEl) { - // Read the dom to determine if the contents of the tab are reversed - // (rotated 180 degrees). If so, we can cache the result becuase - // it's safe to assume all tabs in the tabbar will be the same - if (me._isTabReversed === undefined) { - me._isTabReversed = isTabReversed = // use currentStyle because getComputedStyle won't get the - // filter property in IE9 - (tab.btnWrap.dom.currentStyle.filter.indexOf('rotation=2') !== -1); - } - direction = isTabReversed ? this._reverseDockNames[me.dock] : me.dock; - closeWidth = closeEl.getWidth(); - closeHeight = closeEl.getHeight(); - closeXY = me.getCloseXY(closeEl, tabX, tabY, tabWidth, tabHeight, closeWidth, closeHeight, direction); - closeX = closeXY[0]; - closeY = closeXY[1]; - closeRegion = new Ext.util.Region(closeY, closeX + closeWidth, closeY + closeHeight, closeX); - close = closeRegion.contains(point); - } - break; - } - } - return { - tab: tab, - close: close - }; - }, - /** - * @private - */ - getCloseXY: function(closeEl, tabX, tabY, tabWidth, tabHeight, closeWidth, closeHeight, direction) { - var closeXY = closeEl.getXY(), - closeX, closeY; - if (direction === 'right') { - closeX = tabX + tabWidth - ((closeXY[1] - tabY) + closeHeight); - closeY = tabY + (closeXY[0] - tabX); - } else { - closeX = tabX + (closeXY[1] - tabY); - closeY = tabY + tabX + tabHeight - closeXY[0] - closeWidth; - } - return [ - closeX, - closeY - ]; - }, - /** - * @private - * Closes the given tab by removing it from the TabBar and removing the corresponding card from the TabPanel - * @param {Ext.tab.Tab} toClose The tab to close - */ - closeTab: function(toClose) { - var me = this, - card = toClose.card, - tabPanel = me.tabPanel, - toActivate; - if (card && card.fireEvent('beforeclose', card) === false) { - return false; - } - // If we are closing the active tab, revert to the previously active tab (or the previous or next enabled sibling if - // there *is* no previously active tab, or the previously active tab is the one that's being closed or the previously - // active tab has since been disabled) - toActivate = me.findNextActivatable(toClose); - // We are going to remove the associated card, and then, if that was sucessful, remove the Tab, - // And then potentially activate another Tab. We should not layout for each of these operations. - Ext.suspendLayouts(); - if (tabPanel && card) { - // Remove the ownerCt so the tab doesn't get destroyed if the remove is successful - // We need this so we can have the tab fire it's own close event. - delete toClose.ownerCt; - // we must fire 'close' before removing the card from panel, otherwise - // the event will no loger have any listener - card.fireEvent('close', card); - tabPanel.remove(card); - // Remove succeeded - if (!tabPanel.getComponent(card)) { - /* - * Force the close event to fire. By the time this function returns, - * the tab is already destroyed and all listeners have been purged - * so the tab can't fire itself. - */ - toClose.fireClose(); - me.remove(toClose); - } else { - // Restore the ownerCt from above - toClose.ownerCt = me; - Ext.resumeLayouts(true); - return false; - } - } - // If we are closing the active tab, revert to the previously active tab (or the previous sibling or the nnext sibling) - if (toActivate) { - // Our owning TabPanel calls our setActiveTab method, so only call that if this Bar is being used - // in some other context (unlikely) - if (tabPanel) { - tabPanel.setActiveTab(toActivate.card); - } else { - me.setActiveTab(toActivate); - } - toActivate.focus(); - } - Ext.resumeLayouts(true); - }, - /** - * @private - * Determines the next tab to activate when one tab is closed. - */ - findNextActivatable: function(toClose) { - var me = this, - previousTab = me.previousTab, - nextTab; - if (toClose.active && me.items.getCount() > 1) { - if (previousTab && previousTab !== toClose && !previousTab.disabled) { - nextTab = previousTab; - } else { - nextTab = toClose.next('tab[disabled=false]') || toClose.prev('tab[disabled=false]'); - } - } - // If we couldn't find the next tab to activate, fall back - // to the currently active one. We need to have a focused tab - // at all times. - return nextTab || me.activeTab; - }, - /** - * @private - * Marks the given tab as active - * @param {Ext.tab.Tab} tab The tab to mark active - * @param {Boolean} initial True if we're setting the tab during setup - */ - setActiveTab: function(tab, initial) { - var me = this; - if (!tab.disabled && tab !== me.activeTab) { - // Deactivate the previous tab, and ensure this FocusableContainer knows about it - if (me.activeTab) { - if (me.activeTab.destroyed) { - me.previousTab = null; - } else { - me.previousTab = me.activeTab; - me.activeTab.deactivate(); - me.deactivateFocusable(me.activeTab); - } - } - // Activate the new tab, and ensure this FocusableContainer knows about it - tab.activate(); - me.activateFocusable(tab); - me.activeTab = tab; - me.needsScroll = true; - // We don't fire the change event when setting the first tab. - // Also no need to run a layout - if (!initial) { - me.fireEvent('change', me, tab, tab.card); - // Ensure that after the currently in progress layout, the active tab is scrolled into view - me.updateLayout(); - } - } - }, - privates: { - adjustTabPositions: function() { - var me = this, - items = me.items.items, - i = items.length, - tab, lastBox, el, rotation, prop; - // When tabs are rotated vertically we don't have a reliable way to position - // them using CSS in modern browsers. This is because of the way transform-orign - // works - it requires the width to be known, and the width is not known in css. - // Consequently we have to make an adjustment to the tab's position in these browsers. - // This is similar to what we do in Ext.panel.Header#adjustTitlePosition - if (!Ext.isIE8) { - // 'left' in normal mode, 'right' in rtl - prop = me._getTabAdjustProp(); - while (i--) { - tab = items[i]; - el = tab.el; - lastBox = tab.lastBox; - rotation = tab.isTab ? tab.getActualRotation() : 0; - if (rotation === 1 && tab.isVisible()) { - // rotated 90 degrees using the top left corner as the axis. - // tabs need to be shifted to the right by their width - el.setStyle(prop, (lastBox.x + lastBox.width) + 'px'); - } else if (rotation === 2 && tab.isVisible()) { - // rotated 270 degrees using the bottom right corner as the axis. - // tabs need to be shifted to the left by their height - el.setStyle(prop, (lastBox.x - lastBox.height) + 'px'); - } - } - } - }, - applyTargetCls: function(targetCls) { - this.bodyTargetCls = targetCls; - }, - // rtl hook - _getTabAdjustProp: function() { - return 'left'; - }, - getTargetEl: function() { - return this.body || this.frameBody || this.el; - }, - onClick: function(e, target) { - var me = this, - tabEl, tab, isCloseClick, tabInfo; - if (e.getTarget('.' + Ext.baseCSSPrefix + 'box-scroller')) { - return; - } - if (Ext.isIE8 && me.vertical) { - tabInfo = me.getTabInfoFromPoint(e.getXY()); - tab = tabInfo.tab; - isCloseClick = tabInfo.close; - } else { - // The target might not be a valid tab el. - tabEl = e.getTarget('.' + Ext.tab.Tab.prototype.baseCls); - tab = tabEl && Ext.getCmp(tabEl.id); - isCloseClick = tab && tab.closeEl && (target === tab.closeEl.dom); - } - if (isCloseClick) { - e.preventDefault(); - } - if (tab && tab.isDisabled && !tab.isDisabled()) { - // This will focus the tab; we do it before activating the card - // because the card may attempt to focus itself or a child item. - // We need to focus the tab explicitly because click target is - // the Bar, not the Tab. - tab.beforeClick(isCloseClick); - if (tab.closable && isCloseClick) { - tab.onCloseClick(); - } else { - me.doActivateTab(tab); - } - } - }, - doActivateTab: function(tab) { - var tabPanel = this.tabPanel; - if (tabPanel) { - // TabPanel will call setActiveTab of the TabBar - if (!tab.disabled) { - tabPanel.setActiveTab(tab.card); - } - } else { - this.setActiveTab(tab); - } - }, - onFocusableContainerFocus: function(e) { - var me = this, - mixin = me.mixins.focusablecontainer, - child; - child = mixin.onFocusableContainerFocus.call(me, e); - if (child && child.isTab) { - me.doActivateTab(child); - } - }, - onFocusableContainerFocusEnter: function(e) { - var me = this, - mixin = me.mixins.focusablecontainer, - child; - child = mixin.onFocusableContainerFocusEnter.call(me, e); - if (child && child.isTab) { - me.doActivateTab(child); - } - }, - focusChild: function(child, forward) { - var me = this, - mixin = me.mixins.focusablecontainer, - nextChild; - nextChild = mixin.focusChild.call(me, child, forward); - if (me.activateOnFocus && nextChild && nextChild.isTab) { - me.doActivateTab(nextChild); - } - } - } -}); - -/** - * A basic tab container. TabPanels can be used exactly like a standard {@link Ext.panel.Panel} for - * layout purposes, but also have special support for containing child Components - * (`{@link Ext.container.Container#cfg-items items}`) that are managed using a - * {@link Ext.layout.container.Card CardLayout layout manager}, and displayed as separate tabs. - * - * **Note:** By default, a tab's close tool _destroys_ the child tab Component and all its descendants. - * This makes the child tab Component, and all its descendants **unusable**. To enable re-use of a tab, - * configure the TabPanel with `{@link #autoDestroy autoDestroy: false}`. - * - * ## TabPanel's layout - * - * TabPanels use a Dock layout to position the {@link Ext.tab.Bar TabBar} at the top of the widget. - * Panels added to the TabPanel will have their header hidden by default because the Tab will - * automatically take the Panel's configured title and icon. - * - * TabPanels use their {@link Ext.panel.Header header} or {@link Ext.panel.Panel#fbar footer} - * element (depending on the {@link #tabPosition} configuration) to accommodate the tab selector buttons. - * This means that a TabPanel will not display any configured title, and will not display any configured - * header {@link Ext.panel.Panel#tools tools}. - * - * To display a header, embed the TabPanel in a {@link Ext.panel.Panel Panel} which uses - * `{@link Ext.container.Container#layout layout: 'fit'}`. - * - * ## Controlling tabs - * - * Configuration options for the {@link Ext.tab.Tab} that represents the component can be passed in - * by specifying the tabConfig option: - * - * @example - * Ext.tip.QuickTipManager.init(); - * Ext.create('Ext.tab.Panel', { - * width: 400, - * height: 400, - * renderTo: document.body, - * items: [{ - * title: 'Foo' - * }, { - * title: 'Bar', - * tabConfig: { - * title: 'Custom Title', - * tooltip: 'A button tooltip' - * } - * }] - * }); - * - * ## Vetoing Changes - * - * User interaction when changing the tabs can be vetoed by listening to the {@link #beforetabchange} event. - * By returning `false`, the tab change will not occur. - * - * @example - * Ext.create('Ext.tab.Panel', { - * renderTo: Ext.getBody(), - * width: 200, - * height: 200, - * listeners: { - * beforetabchange: function(tabs, newTab, oldTab) { - * return newTab.title != 'P2'; - * } - * }, - * items: [{ - * title: 'P1' - * }, { - * title: 'P2' - * }, { - * title: 'P3' - * }] - * }); - * - * # Examples - * - * Here is a basic TabPanel rendered to the body. This also shows the useful configuration {@link #activeTab}, - * which allows you to set the active tab on render. - * - * @example - * Ext.create('Ext.tab.Panel', { - * width: 300, - * height: 200, - * activeTab: 0, - * items: [ - * { - * title: 'Tab 1', - * bodyPadding: 10, - * html : 'A simple tab' - * }, - * { - * title: 'Tab 2', - * html : 'Another one' - * } - * ], - * renderTo : Ext.getBody() - * }); - * - * It is easy to control the visibility of items in the tab bar. Specify hidden: true to have the - * tab button hidden initially. Items can be subsequently hidden and show by accessing the - * tab property on the child item. - * - * @example - * var tabs = Ext.create('Ext.tab.Panel', { - * width: 400, - * height: 400, - * renderTo: document.body, - * items: [{ - * title: 'Home', - * html: 'Home', - * itemId: 'home' - * }, { - * title: 'Users', - * html: 'Users', - * itemId: 'users', - * hidden: true - * }, { - * title: 'Tickets', - * html: 'Tickets', - * itemId: 'tickets' - * }] - * }); - * - * Ext.defer(function(){ - * tabs.child('#home').tab.hide(); - * var users = tabs.child('#users'); - * users.tab.show(); - * tabs.setActiveTab(users); - * }, 1000); - * - * You can remove the background of the TabBar by setting the {@link #plain} property to `true`. - * - * @example - * Ext.create('Ext.tab.Panel', { - * width: 300, - * height: 200, - * activeTab: 0, - * plain: true, - * items: [ - * { - * title: 'Tab 1', - * bodyPadding: 10, - * html : 'A simple tab' - * }, - * { - * title: 'Tab 2', - * html : 'Another one' - * } - * ], - * renderTo : Ext.getBody() - * }); - * - * Another useful configuration of TabPanel is {@link #tabPosition}. This allows you to change the - * position where the tabs are displayed. The available options for this are `'top'` (default) and - * `'bottom'`. - * - * @example - * Ext.create('Ext.tab.Panel', { - * width: 300, - * height: 200, - * activeTab: 0, - * bodyPadding: 10, - * tabPosition: 'bottom', - * items: [ - * { - * title: 'Tab 1', - * html : 'A simple tab' - * }, - * { - * title: 'Tab 2', - * html : 'Another one' - * } - * ], - * renderTo : Ext.getBody() - * }); - * - * The {@link #setActiveTab} is a very useful method in TabPanel which will allow you to change the - * current active tab. You can either give it an index or an instance of a tab. For example: - * - * @example - * var tabs = Ext.create('Ext.tab.Panel', { - * items: [ - * { - * id : 'my-tab', - * title: 'Tab 1', - * html : 'A simple tab' - * }, - * { - * title: 'Tab 2', - * html : 'Another one' - * } - * ], - * renderTo : Ext.getBody() - * }); - * - * var tab = Ext.getCmp('my-tab'); - * - * Ext.create('Ext.button.Button', { - * renderTo: Ext.getBody(), - * text : 'Select the first tab', - * scope : this, - * handler : function() { - * tabs.setActiveTab(tab); - * } - * }); - * - * Ext.create('Ext.button.Button', { - * text : 'Select the second tab', - * scope : this, - * handler : function() { - * tabs.setActiveTab(1); - * }, - * renderTo : Ext.getBody() - * }); - * - * The {@link #getActiveTab} is a another useful method in TabPanel which will return the current active tab. - * - * @example - * var tabs = Ext.create('Ext.tab.Panel', { - * items: [ - * { - * title: 'Tab 1', - * html : 'A simple tab' - * }, - * { - * title: 'Tab 2', - * html : 'Another one' - * } - * ], - * renderTo : Ext.getBody() - * }); - * - * Ext.create('Ext.button.Button', { - * text : 'Get active tab', - * scope : this, - * handler : function() { - * var tab = tabs.getActiveTab(); - * alert('Current tab: ' + tab.title); - * }, - * renderTo : Ext.getBody() - * }); - * - * Adding a new tab is very simple with a TabPanel. You simple call the {@link #method-add} method with an config - * object for a panel. - * - * @example - * var tabs = Ext.create('Ext.tab.Panel', { - * items: [ - * { - * title: 'Tab 1', - * html : 'A simple tab' - * }, - * { - * title: 'Tab 2', - * html : 'Another one' - * } - * ], - * renderTo : Ext.getBody() - * }); - * - * Ext.create('Ext.button.Button', { - * text : 'New tab', - * scope : this, - * handler : function() { - * var tab = tabs.add({ - * // we use the tabs.items property to get the length of current items/tabs - * title: 'Tab ' + (tabs.items.length + 1), - * html : 'Another one' - * }); - * - * tabs.setActiveTab(tab); - * }, - * renderTo : Ext.getBody() - * }); - * - * Additionally, removing a tab is very also simple with a TabPanel. You simple call the {@link #method-remove} method - * with an config object for a panel. - * - * @example - * var tabs = Ext.create('Ext.tab.Panel', { - * items: [ - * { - * title: 'Tab 1', - * html : 'A simple tab' - * }, - * { - * id : 'remove-this-tab', - * title: 'Tab 2', - * html : 'Another one' - * } - * ], - * renderTo : Ext.getBody() - * }); - * - * Ext.create('Ext.button.Button', { - * text : 'Remove tab', - * scope : this, - * handler : function() { - * var tab = Ext.getCmp('remove-this-tab'); - * tabs.remove(tab); - * }, - * renderTo : Ext.getBody() - * }); - */ -Ext.define('Ext.tab.Panel', { - extend: 'Ext.panel.Panel', - alias: 'widget.tabpanel', - alternateClassName: [ - 'Ext.TabPanel' - ], - requires: [ - 'Ext.layout.container.Card', - 'Ext.tab.Bar' - ], - config: { - // @cmd-auto-dependency { directRef: 'Ext.tab.Bar' } - /** - * @cfg {Object} tabBar - * Optional configuration object for the internal {@link Ext.tab.Bar}. - * If present, this is passed straight through to the TabBar's constructor - */ - tabBar: undefined, - /** - * @cfg {"top"/"bottom"/"left"/"right"} tabPosition - * The position where the tab strip should be rendered. Possible values are: - * - * - top - * - bottom - * - left - * - right - */ - tabPosition: 'top', - /** - * @cfg {'default'/0/1/2} tabRotation - * The rotation of the tabs. Can be one of the following values: - * - * - `'default'` use the default rotation, depending on {@link #tabPosition} (see below) - * - `0` - no rotation - * - `1` - rotate 90deg clockwise - * - `2` - rotate 90deg counter-clockwise - * - * The default behavior of this config depends on the {@link #tabPosition}: - * - * - `'top'` or `'bottom'` - `0` - * - `'right'` - `1` - * - `'left'` - `2` - */ - tabRotation: 'default', - /** - * @cfg {Boolean} tabStretchMax - * `true` to stretch all tabs to the height of the tallest tab when the tabBar - * is docked horizontally, or the width of the widest tab when the tabBar is - * docked vertically. - */ - tabStretchMax: true - }, - /** - * @cfg {Number} tabBarHeaderPosition - * If specified, the {@link #tabBar} will be rendered as an item of the TabPanel's - * Header and the specified `tabBarHeaderPosition` will be used as the Panel header's - * {@link Ext.panel.Header#itemPosition}. If not specified, the {@link #tabBar} - * will be rendered as a docked item at {@link #tabPosition}. - */ - /** - * @cfg {String/Number} activeItem - * Doesn't apply for {@link Ext.tab.Panel TabPanel}, use {@link #activeTab} instead. - */ - /** - * @cfg {String/Number/Ext.Component} activeTab - * The tab to activate initially. Either an ID, index or the tab component itself. If null, no tab will be set as active. - */ - /** - * @cfg {Ext.enums.Layout/Object} layout - * Optional configuration object for the internal {@link Ext.layout.container.Card card layout}. - * If present, this is passed straight through to the layout's constructor - */ - /** - * @cfg {Boolean} removePanelHeader - * True to instruct each Panel added to the TabContainer to not render its header element. - * This is to ensure that the title of the panel does not appear twice. - */ - removePanelHeader: true, - /** - * @cfg {Boolean} plain - * True to not show the full background on the TabBar. - */ - plain: false, - /** - * @cfg {String} [itemCls='x-tabpanel-child'] - * The class added to each child item of this TabPanel. - */ - itemCls: Ext.baseCSSPrefix + 'tabpanel-child', - /** - * @cfg {Number} minTabWidth - * The minimum width for a tab in the {@link #cfg-tabBar}. - */ - minTabWidth: undefined, - /** - * @cfg {Number} maxTabWidth The maximum width for each tab. - */ - maxTabWidth: undefined, - /** - * @cfg {Boolean} deferredRender - * - * True by default to defer the rendering of child {@link Ext.container.Container#cfg-items items} to the browsers DOM - * until a tab is activated. False will render all contained {@link Ext.container.Container#cfg-items items} as soon as - * the {@link Ext.layout.container.Card layout} is rendered. If there is a significant amount of content or a lot of - * heavy controls being rendered into panels that are not displayed by default, setting this to true might improve - * performance. - * - * The deferredRender property is internally passed to the layout manager for TabPanels ({@link - * Ext.layout.container.Card}) as its {@link Ext.layout.container.Card#deferredRender} configuration value. - * - * **Note**: leaving deferredRender as true means that the content within an unactivated tab will not be available - */ - deferredRender: true, - _defaultTabRotation: { - top: 0, - right: 1, - bottom: 0, - left: 2 - }, - /** - * @event beforetabchange - * Fires before a tab change (activated by {@link #setActiveTab}). Return false in any listener to cancel - * the tabchange - * @param {Ext.tab.Panel} tabPanel The TabPanel - * @param {Ext.Component} newCard The card that is about to be activated - * @param {Ext.Component} oldCard The card that is currently active - */ - /** - * @event tabchange - * Fires when a new tab has been activated (activated by {@link #setActiveTab}). - * @param {Ext.tab.Panel} tabPanel The TabPanel - * @param {Ext.Component} newCard The newly activated item - * @param {Ext.Component} oldCard The previously active item - */ - initComponent: function() { - var me = this, - // Default to 0 if undefined and not null! - activeTab = me.activeTab !== null ? (me.activeTab || 0) : null, - dockedItems = me.dockedItems, - header = me.header, - tabBarHeaderPosition = me.tabBarHeaderPosition, - tabBar = me.getTabBar(), - headerItems; - // Configure the layout with our deferredRender, and with our activeTeb - me.layout = new Ext.layout.container.Card(Ext.apply({ - owner: me, - deferredRender: me.deferredRender, - itemCls: me.itemCls, - activeItem: activeTab - }, me.layout)); - if (tabBarHeaderPosition != null) { - header = me.header = Ext.apply({}, header); - headerItems = header.items = (header.items ? header.items.slice() : []); - header.itemPosition = tabBarHeaderPosition; - headerItems.push(tabBar); - header.hasTabBar = true; - } else { - dockedItems = [].concat(me.dockedItems || []); - dockedItems.push(tabBar); - me.dockedItems = dockedItems; - } - me.callParent(arguments); - // We have to convert the numeric index/string ID config into its component reference - activeTab = me.activeTab = me.getComponent(activeTab); - // Ensure that the active child's tab is rendered in the active UI state - if (activeTab) { - tabBar.setActiveTab(activeTab.tab, true); - } - }, - /** - * @method getTabBar - * Returns the {@link Ext.tab.Bar} associated with this tabPanel. - * @return {Ext.tab.Bar} The tabBar for this tabPanel - */ - /** - * @method setTabBar - * @ignore - */ - onRender: function() { - var items = this.items.items, - len = items.length, - i; - this.callParent(arguments); - // We want to force any view model for the panel to be created. - // This is because we capture various parts about the panel itself (title, icon, etc) - // So we need to be able to use that to populate the tabs. - // In the future, this could be optimized to be a bit smarter to prevent creation when - // not required. - for (i = 0; i < len; ++i) { - items[i].getBind(); - } - }, - /** - * Makes the given card active. Makes it the visible card in the TabPanel's CardLayout and highlights the Tab. - * @param {String/Number/Ext.Component} card The card to make active. Either an ID, index or the component itself. - * @return {Ext.Component} The resulting active child Component. The call may have been vetoed, or otherwise - * modified by an event listener. - */ - setActiveTab: function(card) { - var me = this, - previous; - // Check for a config object - if (!Ext.isObject(card) || card.isComponent) { - card = me.getComponent(card); - } - previous = me.getActiveTab(); - if (card) { - Ext.suspendLayouts(); - // We may be passed a config object, so add it. - // Without doing a layout! - if (!card.isComponent) { - card = me.add(card); - } - if (previous === card || me.fireEvent('beforetabchange', me, card, previous) === false) { - Ext.resumeLayouts(true); - return previous; - } - // MUST set the activeTab first so that the machinery which listens for show doesn't - // think that the show is "driving" the activation and attempt to recurse into here. - me.activeTab = card; - // Attempt to switch to the requested card. Suspend layouts because if that was successful - // we have to also update the active tab in the tab bar which is another layout operation - // and we must coalesce them. - me.layout.setActiveItem(card); - // Read the result of the card layout. Events dear boy, events! - card = me.activeTab = me.layout.getActiveItem(); - // Card switch was not vetoed by an event listener - if (card && card !== previous) { - // Update the active tab in the tab bar and resume layouts. - me.tabBar.setActiveTab(card.tab); - Ext.resumeLayouts(true); - // previous will be undefined or this.activeTab at instantiation - if (previous !== card) { - me.fireEvent('tabchange', me, card, previous); - } - } else // Card switch was vetoed by an event listener. Resume layouts (Nothing should have changed on a veto). - { - Ext.resumeLayouts(true); - } - return card; - } - return previous; - }, - setActiveItem: function(item) { - return this.setActiveTab(item); - }, - /** - * Returns the item that is currently active inside this TabPanel. - * @return {Ext.Component} The currently active item. - */ - getActiveTab: function() { - var me = this, - // Ensure the calculated result references a Component - result = me.getComponent(me.activeTab); - // Sanitize the result in case the active tab is no longer there. - if (result && me.items.indexOf(result) !== -1) { - me.activeTab = result; - } else { - me.activeTab = undefined; - } - return me.activeTab; - }, - applyTabBar: function(tabBar) { - var me = this, - // if we are rendering the tabbar into the panel header, use same alignment - // as header position, and ignore tabPosition. - dock = (me.tabBarHeaderPosition != null) ? me.getHeaderPosition() : me.getTabPosition(); - return new Ext.tab.Bar(Ext.apply({ - ui: me.ui, - dock: dock, - tabRotation: me.getTabRotation(), - vertical: (dock === 'left' || dock === 'right'), - plain: me.plain, - tabStretchMax: me.getTabStretchMax(), - tabPanel: me - }, tabBar)); - }, - updateHeaderPosition: function(headerPosition, oldHeaderPosition) { - var tabBar = this.getTabBar(); - if (tabBar && (this.tabBarHeaderPosition != null)) { - tabBar.setDock(headerPosition); - } - this.callParent([ - headerPosition, - oldHeaderPosition - ]); - }, - updateTabPosition: function(tabPosition) { - var tabBar = this.getTabBar(); - if (tabBar && (this.tabBarHeaderPosition == null)) { - tabBar.setDock(tabPosition); - } - }, - updateTabRotation: function(tabRotation) { - var tabBar = this.getTabBar(); - if (tabBar) { - tabBar.setTabRotation(tabRotation); - } - }, - /** - * @protected - * Makes sure we have a Tab for each item added to the TabPanel - */ - onAdd: function(item, index) { - var me = this, - cfg = Ext.apply({}, item.tabConfig), - tabBar = me.getTabBar(), - ariaDom, - defaultConfig = { - xtype: 'tab', - title: item.title, - icon: item.icon, - iconCls: item.iconCls, - glyph: item.glyph, - ui: tabBar.ui, - card: item, - disabled: item.disabled, - closable: item.closable, - hidden: item.hidden && !item.hiddenByLayout, - // only hide if it wasn't hidden by the layout itself - tooltip: item.tooltip, - tabBar: tabBar, - tabPosition: tabBar.dock, - rotation: tabBar.getTabRotation() - }; - if (item.closeText !== undefined) { - defaultConfig.closeText = item.closeText; - } - cfg = Ext.applyIf(cfg, defaultConfig); - // Create the correspondiong tab in the tab bar - item.tab = me.tabBar.insert(index, cfg); - // We want to force the relationship of the tabpanel to the tab - item.ariaRole = 'tabpanel'; - // Item might be already rendered and then added to the TabPanel - ariaDom = item.ariaEl.dom; - if (ariaDom) { - ariaDom.setAttribute('aria-labelledby', item.tab.id); - } else { - item.ariaRenderAttributes = item.ariaRenderAttributes || {}; - item.ariaRenderAttributes['aria-labelledby'] = item.tab.id; - } - item.on({ - scope: me, - enable: me.onItemEnable, - disable: me.onItemDisable, - beforeshow: me.onItemBeforeShow, - iconchange: me.onItemIconChange, - iconclschange: me.onItemIconClsChange, - glyphchange: me.onItemGlyphChange, - titlechange: me.onItemTitleChange - }); - if (item.isPanel) { - if (me.removePanelHeader) { - if (item.rendered) { - if (item.header) { - item.header.hide(); - } - } else { - item.header = false; - } - } - if (item.isPanel && me.border) { - item.setBorder(false); - } - } - // Force the view model to be created, see onRender - if (me.rendered) { - item.getBind(); - } - // Ensure that there is at least one active tab. This is only needed when adding tabs via a loader config, i.e., there - // may be no pre-existing tabs. Note that we need to check if activeTab was explicitly set to `null` in the tabpanel - // config (which tells the layout not to set an active item), as this is a valid value to mean 'do not set an active tab'. - if (me.rendered && me.loader && me.activeTab === undefined && me.layout.activeItem !== null) { - me.setActiveTab(0); - } - }, - onMove: function(item, fromIdx, toIdx) { - var tabBar = this.getTabBar(); - this.callParent([ - item, - fromIdx, - toIdx - ]); - // If the move of the item.tab triggered the movement of the child Panel, - // then we're done. - if (tabBar.items.indexOf(item.tab) !== toIdx) { - tabBar.move(item.tab, toIdx); - } - }, - /** - * @private - * Enable corresponding tab when item is enabled. - */ - onItemEnable: function(item) { - item.tab.enable(); - }, - /** - * @private - * Disable corresponding tab when item is enabled. - */ - onItemDisable: function(item) { - item.tab.disable(); - }, - /** - * @private - * Sets activeTab before item is shown. - */ - onItemBeforeShow: function(item) { - if (item !== this.activeTab) { - this.setActiveTab(item); - return false; - } - }, - /** - * @private - * Update the tab icon when panel glyph has been set or changed. - */ - onItemGlyphChange: function(item, newGlyph) { - item.tab.setGlyph(newGlyph); - }, - /** - * @private - * Update the tab icon when panel icon has been set or changed. - */ - onItemIconChange: function(item, newIcon) { - item.tab.setIcon(newIcon); - }, - /** - * @private - * Update the tab iconCls when panel iconCls has been set or changed. - */ - onItemIconClsChange: function(item, newIconCls) { - item.tab.setIconCls(newIconCls); - }, - /** - * @private - * Update the tab title when panel title has been set or changed. - */ - onItemTitleChange: function(item, newTitle) { - item.tab.setText(newTitle); - }, - /** - * @private - * Makes sure we remove the corresponding Tab when an item is removed - */ - onRemove: function(item, destroying) { - var me = this; - item.un({ - scope: me, - enable: me.onItemEnable, - disable: me.onItemDisable, - beforeshow: me.onItemBeforeShow, - iconchange: me.onItemIconChange, - iconclschange: me.onItemIconClsChange, - glyphchange: me.onItemGlyphChange, - titlechange: me.onItemTitleChange - }); - if (item.tab && !me.destroying && item.tab.ownerCt === me.tabBar) { - me.tabBar.remove(item.tab); - } - }, - privates: { - /** - * @private - * Unlink the removed child item from its (@link Ext.tab.Tab Tab}. - * - * If we're removing the currently active tab, activate the nearest one. The item is removed when we call super, - * so we can do preprocessing before then to find the card's index - */ - doRemove: function(item, autoDestroy) { - var me = this, - toActivate; - // Destroying, or removing the last item, nothing to activate - if (me.removingAll || me.destroying || me.items.getCount() === 1) { - me.activeTab = null; - } - // Ask the TabBar which tab to activate next. - // Set the active child panel using the index of that tab - else if (item.tab && (toActivate = me.tabBar.items.indexOf(me.tabBar.findNextActivatable(item.tab))) !== -1) { - me.setActiveTab(toActivate); - } - this.callParent(arguments); - if (item.tab) { - // Remove the two references - delete item.tab.card; - delete item.tab; - } - } - } -}); - -/** - * A toolbar that displays hierarchical data from a {@link Ext.data.TreeStore TreeStore} - * as a trail of breadcrumb buttons. Each button represents a node in the store. A click - * on a button will "select" that node in the tree. Non-leaf nodes will display their - * child nodes on a dropdown menu of the corresponding button in the breadcrumb trail, - * and a click on an item in the menu will trigger selection of the corresponding child - * node. - * - * The selection can be set programmatically using {@link #setSelection}, or retrieved - * using {@link #getSelection}. - */ -Ext.define('Ext.toolbar.Breadcrumb', { - extend: 'Ext.Container', - xtype: 'breadcrumb', - requires: [ - 'Ext.data.TreeStore', - 'Ext.button.Split' - ], - mixins: [ - 'Ext.util.FocusableContainer' - ], - isBreadcrumb: true, - baseCls: Ext.baseCSSPrefix + 'breadcrumb', - layout: 'hbox', - config: { - /** - * @cfg {String} [buttonUI='plain-toolbar'] - * Button UI to use for breadcrumb items. Use {@link #extjs-breadcrumb-ui} to - * add special styling to the breadcrumb arrows - */ - buttonUI: 'plain-toolbar', - /** - * @cfg {String} - * The name of the field in the data model to display in the navigation items of - * this breadcrumb toolbar - */ - displayField: 'text', - /** - * @cfg {String} [overflowHandler=null] - * The overflowHandler for this Breadcrumb: - * - * - `null` - hidden overflow - * - `'scroller'` to render left/right scroller buttons on either side of the breadcrumb - * - `'menu'` to render the overflowing buttons as items of an overflow menu. - */ - overflowHandler: null, - /** - * @cfg {Boolean} [showIcons=null] - * - * Controls whether or not icons of tree nodes are displayed in the breadcrumb - * buttons. There are 3 possible values for this config: - * - * 1. unspecified (`null`) - the default value. In this mode only icons that are - * specified in the tree data using ({@link Ext.data.NodeInterface#icon icon} - * or {@link Ext.data.NodeInterface#iconCls iconCls} will be displayed, but the - * default "folder" and "leaf" icons will not be displayed. - * - * 2. `true` - Icons specified in the tree data will be displayed, and the default - * "folder" and "leaf" icons will be displayed for nodes that do not specify - * an `icon` or `iconCls`. - * - * 3. `false` - No icons will be displayed in the breadcrumb buttons, only text. - */ - showIcons: null, - /** - * @cfg {Boolean} [showMenuIcons=null] - * - * Controls whether or not icons of tree nodes are displayed in the breadcrumb - * menu items. There are 3 possible values for this config: - * - * 1. unspecified (`null`) - the default value. In this mode only icons that are - * specified in the tree data using ({@link Ext.data.NodeInterface#icon icon} - * or {@link Ext.data.NodeInterface#iconCls iconCls} will be displayed, but the - * default "folder" and "leaf" icons will not be displayed. - * - * 2. `true` - Icons specified in the tree data will be displayed, and the default - * "folder" and "leaf" icons will be displayed for nodes that do not specify - * an `icon` or `iconCls`. - * - * 3. `false` - No icons will be displayed on the breadcrumb menu items, only text. - */ - showMenuIcons: null, - /** - * @cfg {Ext.data.TreeStore} store - * The TreeStore that this breadcrumb toolbar should use as its data source - */ - store: null, - /** - * @cfg {Boolean} [useSplitButtons=true] - * `false` to use regular {@link Ext.button.Button Button}s instead of {@link - * Ext.button.Split Split Buttons}. When `true`, a click on the body of a button - * will navigate to the specified node, and a click on the arrow will show a menu - * containing the the child nodes. When `false`, the only mode of navigation is - * the menu, since a click anywhere on the button will show the menu. - */ - useSplitButtons: true - }, - renderConfig: { - /** - * @cfg {Ext.data.TreeModel/String} selection - * The selected node, or `"root"` to select the root node - * @accessor - */ - selection: 'root' - }, - publishes: [ - 'selection' - ], - twoWayBindable: [ - 'selection' - ], - _breadcrumbCls: Ext.baseCSSPrefix + 'breadcrumb', - _btnCls: Ext.baseCSSPrefix + 'breadcrumb-btn', - _folderIconCls: Ext.baseCSSPrefix + 'breadcrumb-icon-folder', - _leafIconCls: Ext.baseCSSPrefix + 'breadcrumb-icon-leaf', - initComponent: function() { - var me = this, - layout = me.layout, - overflowHandler = me.getOverflowHandler(); - if (typeof layout === 'string') { - layout = { - type: layout - }; - } - if (overflowHandler) { - layout.overflowHandler = overflowHandler; - } - me.layout = layout; - // set defaultButtonUI for possible menu overflow handler. - me.defaultButtonUI = me.getButtonUI(); - /** - * Internal cache of buttons that are re-purposed as the items of this container - * as navigation occurs. - * @property {Ext.button.Split[]} buttons - * @private - */ - me._buttons = []; - me.addCls([ - me._breadcrumbCls, - me._breadcrumbCls + '-' + me.ui - ]); - me.callParent(); - }, - onDestroy: function() { - var me = this; - me._buttons = Ext.destroy(me._buttons); - me.setStore(null); - me.callParent(); - }, - onRemove: function(component, destroying) { - this.callParent([ - component, - destroying - ]); - delete component._breadcrumbNodeId; - }, - afterComponentLayout: function() { - var me = this, - overflowHandler = me.layout.overflowHandler; - me.callParent(arguments); - if (overflowHandler && me.tooNarrow && overflowHandler.scrollToItem) { - overflowHandler.scrollToItem(me.getSelection().get('depth')); - } - }, - /** - * @method getSelection - * Returns the currently selected {@link Ext.data.TreeModel node}. - * @return {Ext.data.TreeModel} node The selected node (or null if there is no - * selection). - */ - /** - * @method setSelection - * Selects the passed {@link Ext.data.TreeModel node} in the breadcrumb component. - * @param {Ext.data.TreeModel} node The node in the breadcrumb {@link #store} to - * select as the active node. - * @return {Ext.toolbar.Breadcrumb} this The breadcrumb component - */ - applySelection: function(node) { - var store = this.getStore(); - if (store) { - node = (node === 'root') ? this.getStore().getRoot() : node; - } else { - node = null; - } - return node; - }, - updateSelection: function(node, prevNode) { - var me = this, - buttons = me._buttons, - items = [], - itemCount = me.items.getCount(), - needsSync = me._needsSync, - displayField = me.getDisplayField(), - showIcons, glyph, iconCls, icon, newItemCount, currentNode, text, button, id, depth, i; - Ext.suspendLayouts(); - if (node) { - currentNode = node; - depth = node.get('depth'); - newItemCount = depth + 1; - i = depth; - while (currentNode) { - id = currentNode.getId(); - button = buttons[i]; - if (!needsSync && button && button._breadcrumbNodeId === id) { - // reached a level in the hierarchy where we are already in sync. - break; - } - text = currentNode.get(displayField); - if (button) { - // If we already have a button for this depth in the button cache reuse it - button.setText(text); - } else { - // no button in the cache - make one and add it to the cache - button = buttons[i] = Ext.create({ - xtype: me.getUseSplitButtons() ? 'splitbutton' : 'button', - ui: me.getButtonUI(), - cls: me._btnCls + ' ' + me._btnCls + '-' + me.ui, - text: text, - showEmptyMenu: true, - // begin with an empty menu - items are populated on beforeshow - menu: { - listeners: { - click: '_onMenuClick', - beforeshow: '_onMenuBeforeShow', - scope: this - } - }, - handler: '_onButtonClick', - scope: me - }); - } - showIcons = this.getShowIcons(); - if (showIcons !== false) { - glyph = currentNode.get('glyph'); - icon = currentNode.get('icon'); - iconCls = currentNode.get('iconCls'); - if (glyph) { - button.setGlyph(glyph); - button.setIcon(null); - button.setIconCls(iconCls); - } - // may need css to get glyph - else if (icon) { - button.setGlyph(null); - button.setIconCls(null); - button.setIcon(icon); - } else if (iconCls) { - button.setGlyph(null); - button.setIcon(null); - button.setIconCls(iconCls); - } else if (showIcons) { - // only show default icons if showIcons === true - button.setGlyph(null); - button.setIcon(null); - button.setIconCls((currentNode.isLeaf() ? me._leafIconCls : me._folderIconCls) + '-' + me.ui); - } else { - // if showIcons is null do not show default icons - button.setGlyph(null); - button.setIcon(null); - button.setIconCls(null); - } - } - button.setArrowVisible(currentNode.hasChildNodes()); - button._breadcrumbNodeId = currentNode.getId(); - currentNode = currentNode.parentNode; - i--; - } - if (newItemCount > itemCount) { - // new selection has more buttons than existing selection, add the new buttons - items = buttons.slice(itemCount, depth + 1); - me.add(items); - } else { - // new selection has fewer buttons, remove the extra ones from the items, but - // do not destroy them, as they are returned to the cache and recycled. - for (i = itemCount - 1; i >= newItemCount; i--) { - me.remove(me.items.items[i], false); - } - } - } else { - // null selection - me.removeAll(false); - } - Ext.resumeLayouts(true); - /** - * @event selectionchange - * Fires when the selected node changes. At render time, this event will fire - * indicating that the configured {@link #selection} has been selected. - * @param {Ext.toolbar.Breadcrumb} this - * @param {Ext.data.TreeModel} node The selected node. - * @param {Ext.data.TreeModel} prevNode The previously selected node. - */ - me.fireEvent('selectionchange', me, node, prevNode); - if (me._shouldFireChangeEvent) { - /** - * @event change - * Fires when the user changes the selected record. In contrast to the {@link #selectionchange} event, this does - * *not* fire at render time, only in response to user activity. - * @param {Ext.toolbar.Breadcrumb} this - * @param {Ext.data.TreeModel} node The selected node. - * @param {Ext.data.TreeModel} prevNode The previously selected node. - */ - me.fireEvent('change', me, node, prevNode); - } - me._shouldFireChangeEvent = true; - me._needsSync = false; - }, - applyUseSplitButtons: function(useSplitButtons, oldUseSplitButtons) { - if (this.rendered && useSplitButtons !== oldUseSplitButtons) { - Ext.raise("Cannot reconfigure 'useSplitButtons' config of Ext.toolbar.Breadcrumb after initial render"); - } - return useSplitButtons; - }, - applyStore: function(store) { - if (store) { - store = Ext.data.StoreManager.lookup(store); - } - return store; - }, - updateStore: function(store, oldStore) { - this._needsSync = true; - if (store && !this.isConfiguring) { - this.setSelection(store.getRoot()); - } - }, - updateOverflowHandler: function(overflowHandler) { - if (overflowHandler === 'menu') { - Ext.raise("Using Menu overflow with breadcrumb is not currently supported."); - } - }, - privates: { - /** - * Handles a click on a breadcrumb button - * @private - * @param {Ext.button.Split} button - * @param {Ext.event.Event} e - */ - _onButtonClick: function(button, e) { - if (this.getUseSplitButtons()) { - this.setSelection(this.getStore().getNodeById(button._breadcrumbNodeId)); - } - }, - /** - * Handles a click on a button menu - * @private - * @param {Ext.menu.Menu} menu - * @param {Ext.menu.Item} item - * @param {Ext.event.Event} e - */ - _onMenuClick: function(menu, item, e) { - if (item) { - this.setSelection(this.getStore().getNodeById(item._breadcrumbNodeId)); - } - }, - /** - * Handles the `beforeshow` event of a button menu - * @private - * @param {Ext.menu.Menu} menu - */ - _onMenuBeforeShow: function(menu) { - var me = this, - node = me.getStore().getNodeById(menu.ownerCmp._breadcrumbNodeId), - displayField = me.getDisplayField(), - showMenuIcons = me.getShowMenuIcons(), - childNodes, child, glyph, items, i, icon, iconCls, ln, item; - if (node.hasChildNodes()) { - childNodes = node.childNodes; - items = []; - for (i = 0 , ln = childNodes.length; i < ln; i++) { - child = childNodes[i]; - item = { - text: child.get(displayField), - _breadcrumbNodeId: child.getId() - }; - if (showMenuIcons !== false) { - glyph = child.get('glyph'); - icon = child.get('icon'); - iconCls = child.get('iconCls'); - if (glyph) { - item.glyph = glyph; - item.iconCls = iconCls; - } - // may need css to get glyph - else if (icon) { - item.icon = icon; - } else if (iconCls) { - item.iconCls = iconCls; - } else if (showMenuIcons) { - // only show default icons if showIcons === true - item.iconCls = (child.isLeaf() ? me._leafIconCls : me._folderIconCls) + '-' + me.ui; - } - } - items.push(item); - } - menu.removeAll(); - menu.add(items); - } else { - // prevent menu from being shown for nodes with no children - return false; - } - } - } -}); - -/** - * A non-rendering placeholder item which instructs the Toolbar's Layout to begin using - * the right-justified button container. - * - * @example - * Ext.create('Ext.panel.Panel', { - * title: 'Toolbar Fill Example', - * width: 300, - * height: 200, - * tbar : [ - * 'Item 1', - * { xtype: 'tbfill' }, - * 'Item 2' - * ], - * renderTo: Ext.getBody() - * }); - */ -Ext.define('Ext.toolbar.Fill', { - extend: 'Ext.Component', - // Toolbar required here because we'll try to decorate it's alternateClassName - // with this class' alternate name - requires: [ - 'Ext.toolbar.Toolbar' - ], - alias: 'widget.tbfill', - alternateClassName: 'Ext.Toolbar.Fill', - ariaRole: 'presentation', - /** - * @property {Boolean} isFill - * `true` in this class to identify an object as an instantiated Fill, or subclass thereof. - */ - isFill: true, - flex: 1 -}); - -/** - * A simple element that adds extra horizontal space between items in a toolbar. - * By default a 2px wide space is added via CSS specification: - * - * .x-toolbar .x-toolbar-spacer { - * width: 2px; - * } - * - * Example: - * - * @example - * Ext.create('Ext.panel.Panel', { - * title: 'Toolbar Spacer Example', - * width: 300, - * height: 200, - * tbar : [ - * 'Item 1', - * { xtype: 'tbspacer' }, // or ' ' - * 'Item 2', - * // space width is also configurable via javascript - * { xtype: 'tbspacer', width: 50 }, // add a 50px space - * 'Item 3' - * ], - * renderTo: Ext.getBody() - * }); - */ -Ext.define('Ext.toolbar.Spacer', { - extend: 'Ext.Component', - // Toolbar required here because we'll try to decorate it's alternateClassName - // with this class' alternate name - requires: [ - 'Ext.toolbar.Toolbar' - ], - alias: 'widget.tbspacer', - alternateClassName: 'Ext.Toolbar.Spacer', - baseCls: Ext.baseCSSPrefix + 'toolbar-spacer', - ariaRole: 'presentation' -}); - -/** - * Provides indentation and folder structure markup for a Tree taking into account - * depth and position within the tree hierarchy. - */ -Ext.define('Ext.tree.Column', { - extend: 'Ext.grid.column.Column', - alias: 'widget.treecolumn', - tdCls: Ext.baseCSSPrefix + 'grid-cell-treecolumn', - autoLock: true, - lockable: false, - draggable: false, - hideable: false, - iconCls: Ext.baseCSSPrefix + 'tree-icon', - checkboxCls: Ext.baseCSSPrefix + 'tree-checkbox', - elbowCls: Ext.baseCSSPrefix + 'tree-elbow', - expanderCls: Ext.baseCSSPrefix + 'tree-expander', - textCls: Ext.baseCSSPrefix + 'tree-node-text', - innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-treecolumn', - customIconCls: Ext.baseCSSPrefix + 'tree-icon-custom', - isTreeColumn: true, - cellTpl: [ - '', - '
    lineempty" role="presentation">
    ', - '
    ', - '
    -end-plus {expanderCls}" role="presentation">
    ', - '', - '
    {checkboxCls}-checked">
    ', - '
    ', - '', - ' role="presentation" class="{childCls} {baseIconCls} {customIconCls} ', - '{baseIconCls}-leafparent-expandedparent {iconCls}" ', - 'style="background-image:url({icon})"/>>', - '', - '{value}', - '', - '{value}', - '' - ], - // fields that will trigger a change in the ui that aren't likely to be bound to a column - uiFields: { - checked: 1, - icon: 1, - iconCls: 1 - }, - // fields that requires a full row render - rowFields: { - expanded: 1, - loaded: 1, - expandable: 1, - leaf: 1, - loading: 1, - qtip: 1, - qtitle: 1, - cls: 1 - }, - initComponent: function() { - var me = this; - me.rendererScope = me.scope; - me.setupRenderer(); - // This always uses its own renderer. - // Any custom renderer is used as an inner renderer to produce the text node of a tree cell. - me.innerRenderer = me.renderer; - me.renderer = me.treeRenderer; - me.callParent(); - me.scope = me; - me.hasCustomRenderer = me.innerRenderer && me.innerRenderer.length > 1; - }, - treeRenderer: function(value, metaData, record, rowIdx, colIdx, store, view) { - var me = this, - cls = record.get('cls'), - rendererData; - // The initial render will inject the cls into the TD's attributes. - // If cls is ever *changed*, then the full rendering path is followed. - if (metaData && cls) { - metaData.tdCls += ' ' + cls; - } - rendererData = me.initTemplateRendererData(value, metaData, record, rowIdx, colIdx, store, view); - return me.getTpl('cellTpl').apply(rendererData); - }, - initTemplateRendererData: function(value, metaData, record, rowIdx, colIdx, store, view) { - var me = this, - innerRenderer = me.innerRenderer, - data = record.data, - parent = record.parentNode, - rootVisible = view.rootVisible, - lines = [], - parentData; - while (parent && (rootVisible || parent.data.depth > 0)) { - parentData = parent.data; - lines[rootVisible ? parentData.depth : parentData.depth - 1] = parentData.isLast ? 0 : 1; - parent = parent.parentNode; - } - return { - record: record, - baseIconCls: me.iconCls, - customIconCls: (data.icon || data.iconCls) ? me.customIconCls : '', - iconCls: data.iconCls, - icon: data.icon, - checkboxCls: me.checkboxCls, - checked: data.checked, - elbowCls: me.elbowCls, - expanderCls: me.expanderCls, - textCls: me.textCls, - leaf: data.leaf, - expandable: record.isExpandable(), - expanded: data.expanded, - isLast: record.isLastVisible(), - blankUrl: Ext.BLANK_IMAGE_URL, - href: data.href, - hrefTarget: data.hrefTarget, - lines: lines, - metaData: metaData, - // subclasses or overrides can implement a getChildCls() method, which can - // return an extra class to add to all of the cell's child elements (icon, - // expander, elbow, checkbox). This is used by the rtl override to add the - // "x-rtl" class to these elements. - childCls: me.getChildCls ? me.getChildCls() + ' ' : '', - value: innerRenderer ? innerRenderer.apply(me.rendererScope, arguments) : value - }; - }, - shouldUpdateCell: function(record, changedFieldNames) { - // For the TreeColumn, if any of the known tree column UI affecting fields are updated - // the cell should be updated in whatever way. - // 1 if a custom renderer (not our default tree cell renderer), else 2. - var me = this, - i = 0, - len, field; - if (changedFieldNames) { - len = changedFieldNames.length; - for (; i < len; ++i) { - field = changedFieldNames[i]; - // Check for fields which always require a full row update. - if (me.rowFields[field]) { - return 1; - } - // Check for fields which require this column to be updated. - // The TreeColumn's treeRenderer is not a custom renderer. - if (me.uiFields[field]) { - return 2; - } - } - } - return me.callParent([ - record, - changedFieldNames - ]); - } -}); - -/** - * @class Ext.tree.NavigationModel - * @private - * This class listens for key events fired from a {@link Ext.tree.Panel TreePanel}, and moves the currently focused item - * by adding the class {@link #focusCls}. - * - * Navigation and interactions are defined by http://www.w3.org/TR/2013/WD-wai-aria-practices-20130307/#TreeView - * or, if there are multiple visible columns, by http://www.w3.org/TR/2013/WD-wai-aria-practices-20130307/#treegrid - */ -Ext.define('Ext.tree.NavigationModel', { - extend: 'Ext.grid.NavigationModel', - alias: 'view.navigation.tree', - initKeyNav: function(view) { - var me = this, - columns = me.view.ownerGrid.columns, - len, i; - // Must go up to any possible locking assembly to find total number of columns - me.isTreeGrid = columns && columns.length > 1; - me.callParent([ - view - ]); - // We will have two keyNavs if we are the navigation model for a lockable assembly - for (i = 0 , len = me.keyNav.length; i < len; i++) { - me.keyNav[i].map.addBinding([ - { - key: '8', - shift: true, - handler: me.onAsterisk, - scope: me - }, - { - key: Ext.event.Event.NUM_MULTIPLY, - handler: me.onAsterisk, - scope: me - } - ]); - } - me.view.grid.on({ - columnschanged: me.onColumnsChanged, - scope: me - }); - }, - onColumnsChanged: function() { - // Must go up to any possible locking assembly to find total number of columns - this.isTreeGrid = this.view.ownerGrid.getVisibleColumnManager().getColumns().length > 1; - }, - onCellClick: function(view, cell, cellIndex, record, row, recordIndex, clickEvent) { - this.callParent([ - view, - cell, - cellIndex, - record, - row, - recordIndex, - clickEvent - ]); - // Return false if node toggled. - // Do not process the cell click further when we do an expand/collapse - return !clickEvent.nodeToggled; - }, - onKeyLeft: function(keyEvent) { - var me = this, - view = keyEvent.view, - record = me.record; - // Left when a TreeGrid navigates between columns - if (me.isTreeGrid && !keyEvent.ctrlKey) { - return me.callParent([ - keyEvent - ]); - } - // Left arrow key on an expanded node closes the node. - if (keyEvent.position.column.isTreeColumn && record.isExpanded()) { - view.collapse(record); - } else // Left arrow key on a closed or end node moves focus to the node's parent (don't attempt to focus hidden root). - { - record = record.parentNode; - if (record && !(record.isRoot() && !view.rootVisible)) { - me.setPosition(record, null, keyEvent); - } - } - }, - onKeyRight: function(keyEvent) { - var me = this, - record = me.record; - // Right when a TreeGrid navigates between columns - if (me.isTreeGrid && !keyEvent.ctrlKey) { - return me.callParent([ - keyEvent - ]); - } - // Right arrow key expands a closed node, moves to the first child of an open node, or does nothing on an end node. - if (!record.isLeaf()) { - if (keyEvent.position.column.isTreeColumn && !record.isExpanded()) { - keyEvent.view.expand(record); - } else if (record.isExpanded()) { - record = record.childNodes[0]; - if (record) { - me.setPosition(record); - } - } - } - }, - onKeyEnter: function(keyEvent) { - if (this.record.data.checked != null) { - this.toggleCheck(keyEvent); - } else { - this.callParent([ - keyEvent - ]); - } - }, - onKeySpace: function(keyEvent) { - if (this.record.data.checked != null) { - this.toggleCheck(keyEvent); - } else { - this.callParent([ - keyEvent - ]); - } - }, - toggleCheck: function(keyEvent) { - this.view.onCheckChange(keyEvent); - }, - // (asterisk) on keypad expands all nodes. - onAsterisk: function(keyEvent) { - this.view.ownerCt.expandAll(); - } -}); - -/** - * Used as a view by {@link Ext.tree.Panel TreePanel}. - */ -Ext.define('Ext.tree.View', { - extend: 'Ext.view.Table', - alias: 'widget.treeview', - config: { - selectionModel: { - type: 'treemodel' - } - }, - /** - * @property {Boolean} isTreeView - * `true` in this class to identify an object as an instantiated TreeView, or subclass thereof. - */ - isTreeView: true, - loadingCls: Ext.baseCSSPrefix + 'grid-tree-loading', - expandedCls: Ext.baseCSSPrefix + 'grid-tree-node-expanded', - leafCls: Ext.baseCSSPrefix + 'grid-tree-node-leaf', - expanderSelector: '.' + Ext.baseCSSPrefix + 'tree-expander', - checkboxSelector: '.' + Ext.baseCSSPrefix + 'tree-checkbox', - expanderIconOverCls: Ext.baseCSSPrefix + 'tree-expander-over', - // Class to add to the node wrap element used to hold nodes when a parent is being - // collapsed or expanded. During the animation, UI interaction is forbidden by testing - // for an ancestor node with this class. - nodeAnimWrapCls: Ext.baseCSSPrefix + 'tree-animator-wrap', - ariaRole: 'tree', - /** - * @cfg {Boolean} - * @inheritdoc - */ - loadMask: false, - /** - * @cfg {Boolean} rootVisible - * False to hide the root node. - */ - rootVisible: true, - /** - * @cfg {Boolean} animate - * True to enable animated expand/collapse (defaults to the value of {@link Ext#enableFx Ext.enableFx}) - */ - expandDuration: 250, - collapseDuration: 250, - toggleOnDblClick: true, - stripeRows: false, - // treeRowTpl which is inserted into the rowTpl chain before the base rowTpl. Sets tree-specific classes and attributes - treeRowTpl: [ - '{%', - 'this.processRowValues(values);', - 'this.nextTpl.applyOut(values, out, parent);', - '%}', - { - priority: 10, - processRowValues: function(rowValues) { - var record = rowValues.record, - view = rowValues.view; - // We always need to set the qtip/qtitle, because they may have been - // emptied, which means we still need to flush that change to the DOM - // so the old values are overwritten - rowValues.rowAttr['data-qtip'] = record.get('qtip') || ''; - rowValues.rowAttr['data-qtitle'] = record.get('qtitle') || ''; - if (record.isExpanded()) { - rowValues.rowClasses.push(view.expandedCls); - } - if (record.isLeaf()) { - rowValues.rowClasses.push(view.leafCls); - } - if (record.isLoading()) { - rowValues.rowClasses.push(view.loadingCls); - } - } - } - ], - /** - * @event afteritemexpand - * Fires after an item has been visually expanded and is visible in the tree. - * @param {Ext.data.NodeInterface} node The node that was expanded - * @param {Number} index The index of the node - * @param {HTMLElement} item The HTML element for the node that was expanded - */ - /** - * @event afteritemcollapse - * Fires after an item has been visually collapsed and is no longer visible in the tree. - * @param {Ext.data.NodeInterface} node The node that was collapsed - * @param {Number} index The index of the node - * @param {HTMLElement} item The HTML element for the node that was collapsed - */ - /** - * @event nodedragover - * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. - * @param {Ext.data.NodeInterface} targetNode The target node - * @param {String} position The drop position, "before", "after" or "append", - * @param {Object} dragData Data relating to the drag operation - * @param {Ext.event.Event} e The event object for the drag - */ - initComponent: function() { - var me = this; - if (me.bufferedRenderer) { - me.animate = false; - } else if (me.initialConfig.animate === undefined) { - me.animate = Ext.enableFx; - } - me.store = me.panel.getStore(); - me.onRootChange(me.store.getRoot()); - me.animQueue = {}; - me.animWraps = {}; - me.callParent(); - me.store.setRootVisible(me.rootVisible); - me.addRowTpl(Ext.XTemplate.getTpl(me, 'treeRowTpl')); - }, - onFillComplete: function(treeStore, fillRoot, newNodes) { - var me = this, - store = me.store, - start = store.indexOf(newNodes[0]); - // Always update the current node, since the load may be triggered - // by .load() directly instead of .expand() on the node - fillRoot.triggerUIUpdate(); - // In the cases of expand, the records might not be in the store yet, - // so jump out early and expand will handle it later - if (!newNodes.length || start === -1) { - return; - } - // Insert new nodes into the view - me.onAdd(me.store, newNodes, start); - me.refreshPartner(); - }, - refreshPartner: function() { - var partner = this.lockingPartner; - if (partner) { - partner.refresh(); - } - }, - afterComponentLayout: function(width, height, prevWidth, prevHeight) { - var scroller = this.getScrollable(); - this.callParent([ - width, - height, - prevWidth, - prevHeight - ]); - if (scroller && !this.bufferedRenderer) { - scroller.refresh(); - } - }, - processUIEvent: function(e) { - // If the clicked node is part of an animation, ignore the click. - // This is because during a collapse animation, the associated Records - // will already have been removed from the Store, and the event is not processable. - if (e.getTarget('.' + this.nodeAnimWrapCls, this.el)) { - return false; - } - return this.callParent([ - e - ]); - }, - setRootNode: function(node) { - this.node = node; - }, - getChecked: function() { - var checked = []; - this.node.cascadeBy(function(rec) { - if (rec.get('checked')) { - checked.push(rec); - } - }); - return checked; - }, - isItemChecked: function(rec) { - return rec.get('checked'); - }, - /** - * @private - */ - createAnimWrap: function(record, index) { - var me = this, - node = me.getNode(record), - tmpEl; - tmpEl = Ext.fly(node).insertSibling({ - role: 'presentation', - tag: 'div', - cls: me.nodeAnimWrapCls - }, 'after'); - return { - record: record, - node: node, - el: tmpEl, - expanding: false, - collapsing: false, - animateEl: tmpEl, - targetEl: tmpEl - }; - }, - /** - * @private - * Returns the animation wrapper element for the specified parent node, used to wrap the child nodes as - * they slide up or down during expand/collapse. - * - * @param parent The parent node to be expanded or collapsed - * - * @param [bubble=true] If the passed parent node does not already have a wrap element created, by default - * this function will bubble up to each parent node looking for a valid wrap element to reuse, returning - * the first one it finds. This is the appropriate behavior, e.g., for the collapse direction, so that the - * entire expanded set of branch nodes can collapse as a single unit. - * - * However for expanding each parent node should instead always create its own animation wrap if one - * doesn't exist, so that its children can expand independently of any other nodes -- this is crucial - * when executing the "expand all" behavior. If multiple nodes attempt to reuse the same ancestor wrap - * element concurrently during expansion it will lead to problems as the first animation to complete will - * delete the wrap el out from under other running animations. For that reason, when expanding you should - * always pass `bubble: false` to be on the safe side. - * - * If the passed parent has no wrap (or there is no valid ancestor wrap after bubbling), this function - * will return null and the calling code should then call {@link #createAnimWrap} if needed. - * - * @return {Ext.dom.Element} The wrapping element as created in {@link #createAnimWrap}, or null - */ - getAnimWrap: function(parent, bubble) { - if (!this.animate) { - return null; - } - var wraps = this.animWraps, - wrap = wraps[parent.internalId]; - if (bubble !== false) { - while (!wrap && parent) { - parent = parent.parentNode; - if (parent) { - wrap = wraps[parent.internalId]; - } - } - } - return wrap; - }, - doAdd: function(records, index) { - var me = this, - record = records[0], - parent = record.parentNode, - all = me.all, - relativeIndex, - animWrap = me.getAnimWrap(parent), - targetEl, childNodes, len, result, children; - if (!animWrap || !animWrap.expanding) { - return me.callParent([ - records, - index - ]); - } - // If we are adding records which have a parent that is currently expanding - // lets add them to the animation wrap - result = me.bufferRender(records, index, true); - children = result.children; - // We need the parent that has the animWrap, not the node's parent - parent = animWrap.record; - // If there is an anim wrap we do our special magic logic - targetEl = animWrap.targetEl; - childNodes = targetEl.dom.childNodes; - len = childNodes.length; - // The relative index is the index in the full flat collection minus the index of the wraps parent - relativeIndex = index - me.indexInStore(parent) - 1; - // If we are adding records to the wrap that have a higher relative index then there are currently children - // it means we have to append the nodes to the wrap - if (!len || relativeIndex >= len) { - targetEl.appendChild(result.fragment, true); - } else // If there are already more children then the relative index it means we are adding child nodes of - // some expanded node in the anim wrap. In this case we have to insert the nodes in the right location - { - Ext.fly(childNodes[relativeIndex]).insertSibling(children, 'before', true); - } - // We also have to update the node cache of the DataView - all.insert(index, children); - return children; - }, - onRemove: function(ds, records, index) { - var me = this, - empty, i, - fireRemoveEvent = me.hasListeners.remove, - oldItems; - if (me.viewReady) { - empty = me.store.getCount() === 0; - // If buffered rendering is being used, call the parent class. - if (me.bufferedRenderer) { - return me.callParent([ - ds, - records, - index - ]); - } - if (fireRemoveEvent) { - oldItems = this.all.slice(index, index + records.length); - } - // Nothing left, just refresh the view. - if (empty) { - me.refresh(); - } else { - // Remove in reverse order so that indices remain correct - for (i = records.length - 1 , index += i; i >= 0; --i , --index) { - me.doRemove(records[i], index); - } - me.refreshSizePending = true; - } - // Only fire the event if there's anyone listening - if (fireRemoveEvent) { - me.fireEvent('itemremove', records, index, oldItems, me); - } - } - }, - doRemove: function(record, index) { - // If we are adding records which have a parent that is currently expanding - // lets add them to the animation wrap - var me = this, - all = me.all, - animWrap = me.getAnimWrap(record), - item = all.item(index), - node = item ? item.dom : null; - if (!node || !animWrap || !animWrap.collapsing) { - return me.callParent([ - record, - index - ]); - } - // Insert the item at the beginning of the animate el - child nodes are removed - // in reverse order so that the index can be used. - animWrap.targetEl.dom.insertBefore(node, animWrap.targetEl.dom.firstChild); - all.removeElement(index); - }, - onBeforeExpand: function(parent, records, index) { - var me = this, - animWrap; - if (me.rendered && me.all.getCount() && me.animate) { - if (me.getNode(parent)) { - animWrap = me.getAnimWrap(parent, false); - if (!animWrap) { - animWrap = me.animWraps[parent.internalId] = me.createAnimWrap(parent); - animWrap.animateEl.setHeight(0); - } else if (animWrap.collapsing) { - // If we expand this node while it is still expanding then we - // have to remove the nodes from the animWrap. - animWrap.targetEl.select(me.itemSelector).destroy(); - } - animWrap.expanding = true; - animWrap.collapsing = false; - } - } - }, - onExpand: function(parent) { - var me = this, - queue = me.animQueue, - id = parent.getId(), - node = me.getNode(parent), - index = node ? me.indexOf(node) : -1, - animWrap, animateEl, targetEl; - if (me.singleExpand) { - me.ensureSingleExpand(parent); - } - // The item is not visible yet - if (index === -1) { - return; - } - animWrap = me.getAnimWrap(parent, false); - if (!animWrap) { - parent.isExpandingOrCollapsing = false; - me.fireEvent('afteritemexpand', parent, index, node); - return; - } - animateEl = animWrap.animateEl; - targetEl = animWrap.targetEl; - animateEl.stopAnimation(); - queue[id] = true; - // Must set element height before this event finishes because animation does not set - // initial condition until first tick has elapsed. - // Which is good because the upcoming layout resumption must read the content height BEFORE it gets squished. - Ext.on('idle', function() { - animateEl.dom.style.height = '0px'; - }, null, { - single: true - }); - animateEl.animate({ - from: { - height: 0 - }, - to: { - height: targetEl.dom.scrollHeight - }, - duration: me.expandDuration, - listeners: { - afteranimate: function() { - // Move all the nodes out of the anim wrap to their proper location - // Must do this in afteranimate because lastframe does not fire if the - // animation is stopped. - var items = targetEl.dom.childNodes, - activeEl = Ext.Element.getActiveElement(); - if (items.length) { - if (!targetEl.contains(activeEl)) { - activeEl = null; - } - animWrap.el.insertSibling(items, 'before', true); - if (activeEl) { - Ext.fly(activeEl).focus(); - } - } - animWrap.el.destroy(); - me.animWraps[animWrap.record.internalId] = queue[id] = null; - } - }, - callback: function() { - parent.isExpandingOrCollapsing = false; - if (!me.destroyed) { - me.refreshSize(true); - } - me.fireEvent('afteritemexpand', parent, index, node); - } - }); - }, - // Triggered by the TreeStore's beforecollapse event. - onBeforeCollapse: function(parent, records, index, callback, scope) { - var me = this, - animWrap; - if (me.rendered && me.all.getCount()) { - if (me.animate) { - // Only process if the collapsing node is in the UI. - // A node may be collapsed as part of a recursive ancestor collapse, and if it - // has already been removed from the UI by virtue of an ancestor being collapsed, we should not do anything. - if (parent.isVisible()) { - animWrap = me.getAnimWrap(parent); - if (!animWrap) { - animWrap = me.animWraps[parent.internalId] = me.createAnimWrap(parent, index); - } else if (animWrap.expanding) { - // If we collapse this node while it is still expanding then we - // have to remove the nodes from the animWrap. - animWrap.targetEl.select(this.itemSelector).destroy(); - } - animWrap.expanding = false; - animWrap.collapsing = true; - animWrap.callback = callback; - animWrap.scope = scope; - } - } else { - // Cache any passed callback for use in the onCollapse post collapse handler non-animated codepath - me.onCollapseCallback = callback; - me.onCollapseScope = scope; - } - } - }, - onCollapse: function(parent) { - var me = this, - queue = me.animQueue, - id = parent.getId(), - node = me.getNode(parent), - index = node ? me.indexOf(node) : -1, - animWrap = me.getAnimWrap(parent), - animateEl; - // If the collapsed node is already removed from the UI - // by virtue of being a descendant of a collapsed node, then - // we have nothing to do here. - if (!me.all.getCount() || !parent.isVisible()) { - return; - } - // Not animating, all items will have been added, so updateLayout and resume layouts - if (!animWrap) { - parent.isExpandingOrCollapsing = false; - me.fireEvent('afteritemcollapse', parent, index, node); - // Call any collapse callback cached in the onBeforeCollapse handler - Ext.callback(me.onCollapseCallback, me.onCollapseScope); - me.onCollapseCallback = me.onCollapseScope = null; - return; - } - animateEl = animWrap.animateEl; - queue[id] = true; - animateEl.stopAnimation(); - animateEl.animate({ - to: { - height: 0 - }, - duration: me.collapseDuration, - listeners: { - afteranimate: function() { - // In case lastframe did not fire because the animation was stopped. - animWrap.el.destroy(); - me.animWraps[animWrap.record.internalId] = queue[id] = null; - } - }, - callback: function() { - parent.isExpandingOrCollapsing = false; - if (!me.destroyed) { - me.refreshSize(true); - } - me.fireEvent('afteritemcollapse', parent, index, node); - // Call any collapse callback cached in the onBeforeCollapse handler - Ext.callback(animWrap.callback, animWrap.scope); - animWrap.callback = animWrap.scope = null; - } - }); - }, - /** - * Checks if a node is currently undergoing animation - * @private - * @param {Ext.data.Model} node The node - * @return {Boolean} True if the node is animating - */ - isAnimating: function(node) { - return !!this.animQueue[node.getId()]; - }, - /** - * Expands a record that is loaded in the view. - * - * If an animated collapse or expand of the record is in progress, this call will be ignored. - * @param {Ext.data.Model} record The record to expand - * @param {Boolean} [deep] True to expand nodes all the way down the tree hierarchy. - * @param {Function} [callback] The function to run after the expand is completed - * @param {Object} [scope] The scope of the callback function. - */ - expand: function(record, deep, callback, scope) { - var me = this, - doAnimate = !!me.animate, - result; - // Block toggling if we are already animating an expand or collapse operation. - if (!doAnimate || !record.isExpandingOrCollapsing) { - if (!record.isLeaf()) { - record.isExpandingOrCollapsing = doAnimate; - } - // Need to suspend layouts because the expand process makes multiple changes to the UI - // in addition to inserting new nodes. Folder and elbow images have to change, so we - // need to coalesce all resulting layouts. - Ext.suspendLayouts(); - result = record.expand(deep, callback, scope); - Ext.resumeLayouts(true); - return result; - } - }, - /** - * Collapses a record that is loaded in the view. - * - * If an animated collapse or expand of the record is in progress, this call will be ignored. - * @param {Ext.data.Model} record The record to collapse - * @param {Boolean} [deep] True to collapse nodes all the way up the tree hierarchy. - * @param {Function} [callback] The function to run after the collapse is completed - * @param {Object} [scope] The scope of the callback function. - */ - collapse: function(record, deep, callback, scope) { - var me = this, - doAnimate = !!me.animate; - // Block toggling if we are already animating an expand or collapse operation. - if (!doAnimate || !record.isExpandingOrCollapsing) { - if (!record.isLeaf()) { - record.isExpandingOrCollapsing = doAnimate; - } - return record.collapse(deep, callback, scope); - } - }, - /** - * Toggles a record between expanded and collapsed. - * - * If an animated collapse or expand of the record is in progress, this call will be ignored. - * @param {Ext.data.Model} record - * @param {Boolean} [deep] True to collapse nodes all the way up the tree hierarchy. - * @param {Function} [callback] The function to run after the expand/collapse is completed - * @param {Object} [scope] The scope of the callback function. - */ - toggle: function(record, deep, callback, scope) { - if (record.isExpanded()) { - this.collapse(record, deep, callback, scope); - } else { - this.expand(record, deep, callback, scope); - } - }, - onItemDblClick: function(record, item, index, e) { - var me = this, - editingPlugin = me.editingPlugin; - me.callParent([ - record, - item, - index, - e - ]); - if (me.toggleOnDblClick && record.isExpandable() && !(editingPlugin && editingPlugin.clicksToEdit === 2)) { - me.toggle(record); - } - }, - onCellClick: function(cell, cellIndex, record, row, rowIndex, e) { - var me = this, - column = e.position.column, - checkedState; - // We're only interested in clicks in the tree column - if (column.isTreeColumn) { - // Click in the checkbox. - // Allow beforecheckchange event to veto a change of checkbox state - if (e.getTarget(me.checkboxSelector, cell) && Ext.isBoolean(checkedState = record.get('checked')) && me.fireEvent('beforecheckchange', record, checkedState, e) !== false) { - me.onCheckChange(e); - // Allow the stopSelection config on checkable tree columns to prevent selection - if (column.stopSelection) { - e.stopSelection = true; - } - } - // Click on the expander - else if (e.getTarget(me.expanderSelector, cell) && record.isExpandable()) { - // Ensure focus is on the clicked cell so that if this causes a refresh, - // focus restoration does not scroll back to the previouslty focused position. - // onCellClick is called *befor* cellclick is fired which is what changes focus position. - // TODO: connect directly from View's event processing to NavigationModel without relying on events. - me.getNavigationModel().setPosition(e.position); - me.toggle(record, e.ctrlKey); - // So that we know later to stop event propagation by returning false from the NavigationModel - // TODO: when NavigationModel is directly hooked up to be called *before* the event sequence - // This flag will not be necessary. - e.nodeToggled = true; - } - return me.callParent([ - cell, - cellIndex, - record, - row, - rowIndex, - e - ]); - } - }, - onCheckChange: function(e) { - var record = e.record, - checked = !record.get('checked'); - record.set('checked', checked); - this.fireEvent('checkchange', record, checked, e); - }, - onItemMouseOver: function(record, item, index, e) { - if (e.getTarget(this.expanderSelector, item)) { - e.getTarget(this.cellSelector, null, true).addCls(this.expanderIconOverCls); - } - }, - onItemMouseOut: function(record, item, index, e) { - if (e.getTarget(this.expanderSelector, item)) { - e.getTarget(this.cellSelector, null, true).removeCls(this.expanderIconOverCls); - } - }, - getStoreListeners: function() { - return Ext.apply(this.callParent(), { - rootchange: this.onRootChange, - fillcomplete: this.onFillComplete - }); - }, - onBindStore: function(store, initial, propName, oldStore) { - var oldRoot = oldStore && oldStore.getRootNode(), - newRoot = store && store.getRootNode(); - this.callParent([ - store, - initial, - propName, - oldStore - ]); - // The root implicitly changes when reconfigured with a new store. - // The store's own rootChange event when it initially sets its own rootNode - // will not have reached us because it was not ourt store during its initialization. - if (newRoot !== oldRoot) { - this.onRootChange(newRoot, oldRoot); - } - }, - onRootChange: function(newRoot, oldRoot) { - var me = this, - grid = me.grid; - if (oldRoot) { - me.rootListeners.destroy(); - me.rootListeners = null; - } - if (newRoot) { - me.rootListeners = newRoot.on({ - beforeexpand: me.onBeforeExpand, - expand: me.onExpand, - beforecollapse: me.onBeforeCollapse, - collapse: me.onCollapse, - destroyable: true, - scope: me - }); - grid.addRelayers(newRoot); - } - }, - ensureSingleExpand: function(node) { - var parent = node.parentNode; - if (parent) { - parent.eachChild(function(child) { - if (child !== node && child.isExpanded()) { - child.collapse(); - } - }); - } - } -}); - -/** - * The TreePanel provides tree-structured UI representation of tree-structured data. - * A TreePanel must be bound to a {@link Ext.data.TreeStore}. - * - * TreePanels support multiple columns through the {@link #columns} configuration. - * - * By default a TreePanel contains a single column which uses the `text` Field of - * the store's nodes. - * - * Simple TreePanel using inline data: - * - * @example - * var store = Ext.create('Ext.data.TreeStore', { - * root: { - * expanded: true, - * children: [ - * { text: 'detention', leaf: true }, - * { text: 'homework', expanded: true, children: [ - * { text: 'book report', leaf: true }, - * { text: 'algebra', leaf: true} - * ] }, - * { text: 'buy lottery tickets', leaf: true } - * ] - * } - * }); - * - * Ext.create('Ext.tree.Panel', { - * title: 'Simple Tree', - * width: 200, - * height: 200, - * store: store, - * rootVisible: false, - * renderTo: Ext.getBody() - * }); - * - * For the tree node config options (like `text`, `leaf`, `expanded`), see the documentation of - * {@link Ext.data.NodeInterface NodeInterface} config options. - * - * Unless the TreeStore is configured with a {@link Ext.data.Model model} of your choosing, nodes in the {@link Ext.data.TreeStore} are by default, instances of {@link Ext.data.TreeModel}. - * - * # Heterogeneous node types. - * - * If the tree needs to use different data model classes at different levels there is much flexibility in how to specify this. - * - * ### Configuring the Reader. - * If you configure the proxy's reader with a {@link Ext.data.reader.Reader#typeProperty typeProperty}, then the server is in control of which data model - * types are created. A discriminator field is used in the raw data to decide which class to instantiate. - * **If this is configured, then the data from the server is prioritized over other ways of determining node class**. - * - * @example - * Ext.define('myApp.Territory', { - * extend: 'Ext.data.TreeModel', - * fields: [{ - * name: 'text', - * mapping: 'name' - * }] - * }); - * Ext.define('myApp.Country', { - * extend: 'Ext.data.TreeModel', - * fields: [{ - * name: 'text', - * mapping: 'name' - * }] - * }); - * Ext.define('myApp.City', { - * extend: 'Ext.data.TreeModel', - * fields: [{ - * name: 'text', - * mapping: 'name' - * }] - * }); - * Ext.create('Ext.tree.Panel', { - * renderTo: document.body, - * height: 200, - * width: 400, - * title: 'Sales Areas - using typeProperty', - * rootVisible: false, - * store: { - * // Child types use namespace of store's model by default - * model: 'myApp.Territory', - * proxy: { - * type: 'memory', - * reader: { - * typeProperty: 'mtype' - * } - * }, - * root: { - * children: [{ - * name: 'Europe, ME, Africa', - * mtype: 'Territory', - * children: [{ - * name: 'UK of GB & NI', - * mtype: 'Country', - * children: [{ - * name: 'London', - * mtype: 'City', - * leaf: true - * }] - * }] - * }, { - * name: 'North America', - * mtype: 'Territory', - * children: [{ - * name: 'USA', - * mtype: 'Country', - * children: [{ - * name: 'Redwood City', - * mtype: 'City', - * leaf: true - * }] - * }] - * }] - * } - * } - * }); - * - * ### Node being loaded decides. - * You can declare your TreeModel subclasses with a {@link Ext.data.TreeModel#childType childType} which means that the node being loaded decides the - * class to instantiate for all of its child nodes. - * - * It is important to note that if the root node is {@link Ext.tree.Panel#rootVisible hidden}, its type will default to the store's model type, and if left - * as the default (`{@link Ext.data.TreeModel}`) this will have no knowledge of creation of special child node types. So be sure to specify a store model in this case: - * - * @example - * Ext.define('myApp.TerritoryRoot', { - * extend: 'Ext.data.TreeModel', - * childType: 'myApp.Territory', - * fields: [{ - * name: 'text', - * mapping: 'name' - * }] - * }); - * Ext.define('myApp.Territory', { - * extend: 'Ext.data.TreeModel', - * childType: 'myApp.Country', - * fields: [{ - * name: 'text', - * mapping: 'name' - * }] - * }); - * Ext.define('myApp.Country', { - * extend: 'Ext.data.TreeModel', - * childType: 'myApp.City', - * fields: [{ - * name: 'text', - * mapping: 'name' - * }] - * }); - * Ext.define('myApp.City', { - * extend: 'Ext.data.TreeModel', - * fields: [{ - * name: 'text', - * mapping: 'name' - * }] - * }); - * Ext.create('Ext.tree.Panel', { - * renderTo: document.body, - * height: 200, - * width: 400, - * title: 'Sales Areas', - * rootVisible: false, - * store: { - * model: 'myApp.TerritoryRoot', // Needs to be this so it knows to create 'Country' child nodes - * root: { - * children: [{ - * name: 'Europe, ME, Africa', - * children: [{ - * name: 'UK of GB & NI', - * children: [{ - * name: 'London', - * leaf: true - * }] - * }] - * }, { - * name: 'North America', - * children: [{ - * name: 'USA', - * children: [{ - * name: 'Redwood City', - * leaf: true - * }] - * }] - * }] - * } - * } - * }); - * - * # Data structure - * - * The {@link Ext.data.TreeStore TreeStore} maintains a {@link Ext.data.TreeStore#getRoot root node} and a hierarchical structure of {@link Ext.data.TreeModel node}s. - * - * The {@link Ext.tree.View UI} of the tree is driven by a {Ext.data.NodeStore NodeStore} which is a flattened view of *visible* nodes. - * The NodeStore is dynamically updated to reflect the visibility state of nodes as nodes are added, removed or expanded. The UI - * responds to mutation events fire by the NodeStore. - * - * Note that nodes have several more {@link Ext.data.Model#cfg-fields fields} in order to describe their state within the hierarchy. - * - * If you add store listeners to the {@link Ext.data.Store#event-update update} event, then you will receive notification when any of this state changes. - * You should check the array of modified field names passed to the listener to decide whether the listener should take action or ignore the event. - * - * # Tree Grid - * Trees may be configured using the {@link #cfg-columns} config including a - * {@link Ext.tree.Column treecolumn} to give the tree panel a hybrid tree / - * {@link Ext.grid.Panel grid} structure. - * - * @example - * Ext.create({ - * xtype: 'treepanel', - * renderTo: Ext.getBody(), - * height: 200, - * width: 300, - * rootVisible: false, - * store: Ext.create('Ext.data.TreeStore', { - * fields: ['text', 'duration', 'isLayover'], - * root: { - * expanded: true, - * children: [{ - * text: 'SFO  ✈  DFW', - * duration: '6h 55m', - * expanded: true, - * children: [{ - * text: 'SFO  ✈  PHX', - * duration: '2h 04m', - * leaf: true - * }, { - * text: 'PHX layover', - * duration: '2h 36m', - * isLayover: true, - * leaf: true - * }, { - * text: 'PHX  ✈  DFW', - * duration: '2h 15m', - * leaf: true - * }] - * }] - * } - * }), - * columns: [{ - * xtype: 'treecolumn', - * text: 'Flight Endpoints', - * dataIndex: 'text', - * flex: 1, - * renderer: function (val, meta, rec) { - * if (rec.get('isLayover')) { - * meta.tdStyle = 'color: gray; font-style: italic;'; - * } - * return val; - * } - * }, { - * text: 'Duration', - * dataIndex: 'duration', - * width: 100 - * }] - * }); - */ -Ext.define('Ext.tree.Panel', { - extend: 'Ext.panel.Table', - alias: 'widget.treepanel', - alternateClassName: [ - 'Ext.tree.TreePanel', - 'Ext.TreePanel' - ], - requires: [ - 'Ext.tree.View', - 'Ext.selection.TreeModel', - 'Ext.tree.Column', - 'Ext.data.TreeStore', - 'Ext.tree.NavigationModel' - ], - viewType: 'treeview', - treeCls: Ext.baseCSSPrefix + 'tree-panel', - /** - * @cfg {Boolean} [rowLines=false] - * Configure as true to separate rows with visible horizontal lines (depends on theme). - */ - rowLines: false, - /** - * @cfg {Boolean} [lines=true] - * False to disable tree lines. - */ - lines: true, - /** - * @cfg {Boolean} [useArrows=false] - * True to use Vista-style arrows in the tree. - */ - useArrows: false, - /** - * @cfg {Boolean} [singleExpand=false] - * True if only 1 node per branch may be expanded. - */ - singleExpand: false, - ddConfig: { - enableDrag: true, - enableDrop: true - }, - /** - * @cfg {Boolean} animate - * True to enable animated expand/collapse. Defaults to the value of {@link Ext#enableFx}. - */ - /** - * @cfg {Boolean} [rootVisible=true] - * False to hide the root node. - * - * Note that trees *always* have a root node. If you do not specify a {@link #cfg-root} node, one will be created. - * - * If the root node is not visible, then in order for a tree to appear to the end user, the root node is autoloaded with its child nodes. - */ - rootVisible: true, - /** - * @cfg {String} [displayField=text] - * The field inside the model that will be used as the node's text. - */ - displayField: 'text', - /** - * @cfg {Ext.data.Model/Ext.data.TreeModel/Object} root - * Allows you to not specify a store on this TreePanel. This is useful for creating a simple tree with preloaded - * data without having to specify a TreeStore and Model. A store and model will be created and root will be passed - * to that store. For example: - * - * Ext.create('Ext.tree.Panel', { - * title: 'Simple Tree', - * root: { - * text: "Root node", - * expanded: true, - * children: [ - * { text: "Child 1", leaf: true }, - * { text: "Child 2", leaf: true } - * ] - * }, - * renderTo: Ext.getBody() - * }); - */ - root: null, - // Required for the Lockable Mixin. These are the configurations which will be copied to the - // normal and locked sub tablepanels - normalCfgCopy: [ - 'displayField', - 'root', - 'singleExpand', - 'useArrows', - 'lines', - 'rootVisible', - 'scroll' - ], - lockedCfgCopy: [ - 'displayField', - 'root', - 'singleExpand', - 'useArrows', - 'lines', - 'rootVisible' - ], - isTree: true, - /** - * @cfg {Boolean} hideHeaders - * True to hide the headers. - */ - /** - * @cfg {Boolean} folderSort - * True to automatically prepend a leaf sorter to the store. - */ - /** - * @cfg {Ext.data.TreeStore} store (required) - * The {@link Ext.data.TreeStore Store} the tree should use as its data source. - */ - arrowCls: Ext.baseCSSPrefix + 'tree-arrows', - linesCls: Ext.baseCSSPrefix + 'tree-lines', - noLinesCls: Ext.baseCSSPrefix + 'tree-no-lines', - autoWidthCls: Ext.baseCSSPrefix + 'autowidth-table', - constructor: function(config) { - config = config || {}; - if (config.animate === undefined) { - config.animate = Ext.isBoolean(this.animate) ? this.animate : Ext.enableFx; - } - this.enableAnimations = config.animate; - delete config.animate; - this.callParent([ - config - ]); - }, - initComponent: function() { - var me = this, - cls = [ - me.treeCls - ], - store, view; - if (me.useArrows) { - cls.push(me.arrowCls); - me.lines = false; - } - if (me.lines) { - cls.push(me.linesCls); - } else if (!me.useArrows) { - cls.push(me.noLinesCls); - } - store = me.applyStore(me.store); - // If there is no root node defined, then create one. - if (!store.getRoot()) { - store.setRoot({}); - } - // Store must have the same idea about root visibility as us BEFORE callParent binds it. - store.setRootVisible(me.rootVisible); - me.viewConfig = Ext.apply({ - rootVisible: me.rootVisible, - animate: me.enableAnimations, - singleExpand: me.singleExpand, - node: store.getRoot(), - hideHeaders: me.hideHeaders, - navigationModel: 'tree' - }, me.viewConfig); - // If the user specifies the headers collection manually then don't inject - // our own - if (!me.columns) { - if (me.initialConfig.hideHeaders === undefined) { - me.hideHeaders = true; - } - me.addCls(me.autoWidthCls); - me.columns = [ - { - xtype: 'treecolumn', - text: 'Name', - flex: 1, - dataIndex: me.displayField - } - ]; - } - if (me.cls) { - cls.push(me.cls); - } - me.cls = cls.join(' '); - me.callParent(); - view = me.getView(); - // Relay events from the TreeView. - // An injected LockingView relays events from its locked side's View - me.relayEvents(view, [ - /** - * @event beforecheckchange - * Fires when a node with a checkbox's checked property changes. - * @param {Ext.data.TreeModel} node The node who's checked property is to be changed. - * @param {Boolean} checked The node's current checked state. - * @param {Ext.event.Event} e The click event. - */ - 'beforecheckchange', - /** - * @event checkchange - * Fires when a node with a checkbox's checked property changes. - * @param {Ext.data.TreeModel} node The node who's checked property was changed. - * @param {Boolean} checked The node's new checked state. - * @param {Ext.event.Event} e The click event. - */ - 'checkchange', - /** - * @event afteritemexpand - * @inheritdoc Ext.tree.View#afteritemexpand - */ - 'afteritemexpand', - /** - * @event afteritemcollapse - * @inheritdoc Ext.tree.View#afteritemcollapse - */ - 'afteritemcollapse' - ]); - }, - applyStore: function(store) { - // private - // Note that this is not a config system applier. store is not yet a config. - // It just does the job of an applier and converts a config object to the true value - // for the setter to use. - var me = this; - if (Ext.isString(store)) { - store = me.store = Ext.StoreMgr.lookup(store); - } else if (!store || !store.isStore) { - store = Ext.apply({ - type: 'tree', - proxy: 'memory' - }, store); - if (me.root) { - store.root = me.root; - } - if (me.fields) { - store.fields = me.fields; - } else if (me.model) { - store.model = me.model; - } - if (me.folderSort) { - store.folderSort = me.folderSort; - } - store = me.store = Ext.StoreMgr.lookup(store); - } else if (me.root) { - store = me.store = Ext.data.StoreManager.lookup(store); - store.setRoot(me.root); - if (me.folderSort !== undefined) { - store.folderSort = me.folderSort; - store.sort(); - } - } - return store; - }, - setStore: function(store) { - var me = this; - store = me.applyStore(store); - // If there is no rootnode defined, then create one. - if (!store.getRoot()) { - store.setRoot({}); - } - // Store must have the same idea about root visibility as us BEFORE callParent binds it. - store.setRootVisible(me.rootVisible); - if (me.view) { - me.view.setRootNode(store.getRootNode()); - } - me.bindStore(store); - }, - /** - * @private - * Hook into the TreeStore. - */ - bindStore: function(store, initial) { - var me = this, - root = store.getRoot(), - bufferedRenderer = me.bufferedRenderer; - // Bind to store, and autocreate the BufferedRenderer. - me.callParent(arguments); - // If we're in a reconfigure (we already have a BufferedRenderer which is bound to our old store), - // rebind the BufferedRenderer - if (bufferedRenderer) { - if (bufferedRenderer.store) { - bufferedRenderer.bindStore(store); - } - } - // The TreeStore needs to know about this TreePanel's singleExpand constraint so that - // it can ensure the compliance of NodeInterface.expandAll. - store.singleExpand = me.singleExpand; - // Monitor the TreeStore for the root node being changed. Return a Destroyable object - me.storeListeners = me.mon(store, { - destroyable: true, - rootchange: me.onRootChange, - scope: me - }); - // Relay store events. relayEvents always returns a Destroyable object. - me.storeRelayers = me.relayEvents(store, [ - /** - * @event beforeload - * @inheritdoc Ext.data.TreeStore#beforeload - */ - 'beforeload', - /** - * @event load - * @inheritdoc Ext.data.TreeStore#load - */ - 'load' - ]); - // If rootVisible is false, we *might* need to expand the node. - // If store is autoLoad, that will already have been kicked off. - // If its already expanded, or in the process of loading, the TreeStore - // has started that at the end of updateRoot - if (!me.rootVisible && !store.autoLoad && !(root.isExpanded() || root.isLoading())) { - // A hidden root must be expanded, unless it's overridden with autoLoad: false. - // If it's loaded, set its expanded field (silently), and skip ahead to the onNodeExpand callback. - if (root.isLoaded()) { - root.data.expanded = true; - store.onNodeExpand(root, root.childNodes); - } - // Root is not loaded; go through the expand mechanism to force a load - // unless we were told explicitly not to load the store by setting - // autoLoad: false. This is useful with Direct proxy in cases when - // Direct API is loaded dynamically and may not be available at the time - // when TreePanel is created. - else if (store.autoLoad !== false) { - root.data.expanded = false; - root.expand(); - } - } - // TreeStore must have an upward link to the TreePanel so that nodes can find their owning tree in NodeInterface.getOwnerTree - store.ownerTree = me; - if (!initial) { - me.view.setRootNode(root); - } - }, - /** - * @private - */ - addRelayers: function(newRoot) { - var me = this; - if (me.rootRelayers) { - me.rootRelayers.destroy(); - me.rootRelayers = null; - } - // Relay store events with prefix. Return a Destroyable object - me.rootRelayers = me.mon(newRoot, { - destroyable: true, - /** - * @event itemappend - * @inheritdoc Ext.data.TreeStore#nodeappend - */ - append: me.createRelayer('itemappend'), - /** - * @event itemremove - * @inheritdoc Ext.data.TreeStore#noderemove - */ - remove: me.createRelayer('itemremove'), - /** - * @event itemmove - * @inheritdoc Ext.data.TreeStore#nodemove - */ - move: me.createRelayer('itemmove', [ - 0, - 4 - ]), - /** - * @event iteminsert - * @inheritdoc Ext.data.TreeStore#nodeinsert - */ - insert: me.createRelayer('iteminsert'), - /** - * @event beforeitemappend - * @inheritdoc Ext.data.TreeStore#nodebeforeappend - */ - beforeappend: me.createRelayer('beforeitemappend'), - /** - * @event beforeitemremove - * @inheritdoc Ext.data.TreeStore#nodebeforeremove - */ - beforeremove: me.createRelayer('beforeitemremove'), - /** - * @event beforeitemmove - * @inheritdoc Ext.data.TreeStore#nodebeforemove - */ - beforemove: me.createRelayer('beforeitemmove'), - /** - * @event beforeiteminsert - * @inheritdoc Ext.data.TreeStore#nodebeforeinsert - */ - beforeinsert: me.createRelayer('beforeiteminsert'), - /** - * @event itemexpand - * @inheritdoc Ext.data.TreeStore#nodeexpand - */ - expand: me.createRelayer('itemexpand', [ - 0, - 1 - ]), - /** - * @event itemcollapse - * @inheritdoc Ext.data.TreeStore#nodecollapse - */ - collapse: me.createRelayer('itemcollapse', [ - 0, - 1 - ]), - /** - * @event beforeitemexpand - * @inheritdoc Ext.data.TreeStore#nodebeforeexpand - */ - beforeexpand: me.createRelayer('beforeitemexpand', [ - 0, - 1 - ]), - /** - * @event beforeitemcollapse - * @inheritdoc Ext.data.TreeStore#nodebeforecollapse - */ - beforecollapse: me.createRelayer('beforeitemcollapse', [ - 0, - 1 - ]), - scope: me - }); - }, - /** - * @private - */ - unbindStore: function() { - var me = this, - store = me.store; - if (store) { - me.callParent(); - Ext.destroy(me.storeListeners, me.storeRelayers, me.rootRelayers); - delete store.ownerTree; - store.singleExpand = null; - } - }, - /** - * Sets root node of this tree. All trees *always* have a root node. It may be {@link #rootVisible hidden}. - * - * If the passed node has not already been loaded with child nodes, and has its expanded field set, this triggers the {@link #cfg-store} to load the child nodes of the root. - * @param {Ext.data.TreeModel/Object} root - * @return {Ext.data.TreeModel} The new root - */ - setRootNode: function() { - return this.store.setRoot.apply(this.store, arguments); - }, - /** - * Returns the root node for this tree. - * @return {Ext.data.TreeModel} - */ - getRootNode: function() { - return this.store.getRoot(); - }, - onRootChange: function(root) { - this.view.setRootNode(root); - }, - /** - * Retrieve an array of checked records. - * @return {Ext.data.TreeModel[]} An array containing the checked records - */ - getChecked: function() { - return this.getView().getChecked(); - }, - isItemChecked: function(rec) { - return rec.get('checked'); - }, - /** - * Expands a record that is loaded in the tree. - * @param {Ext.data.Model} record The record to expand - * @param {Boolean} [deep] True to expand nodes all the way down the tree hierarchy. - * @param {Function} [callback] The function to run after the expand is completed - * @param {Object} [scope] The scope of the callback function. - */ - expandNode: function(record, deep, callback, scope) { - return this.getView().expand(record, deep, callback, scope || this); - }, - /** - * Collapses a record that is loaded in the tree. - * @param {Ext.data.Model} record The record to collapse - * @param {Boolean} [deep] True to collapse nodes all the way up the tree hierarchy. - * @param {Function} [callback] The function to run after the collapse is completed - * @param {Object} [scope] The scope of the callback function. - */ - collapseNode: function(record, deep, callback, scope) { - return this.getView().collapse(record, deep, callback, scope || this); - }, - /** - * Expand all nodes - * @param {Function} [callback] A function to execute when the expand finishes. - * @param {Object} [scope] The scope of the callback function - */ - expandAll: function(callback, scope) { - var me = this, - root = me.getRootNode(); - if (root) { - Ext.suspendLayouts(); - root.expand(true, callback, scope || me); - Ext.resumeLayouts(true); - } - }, - /** - * Collapse all nodes - * @param {Function} [callback] A function to execute when the collapse finishes. - * @param {Object} [scope] The scope of the callback function - */ - collapseAll: function(callback, scope) { - var me = this, - root = me.getRootNode(), - view = me.getView(); - if (root) { - Ext.suspendLayouts(); - scope = scope || me; - if (view.rootVisible) { - root.collapse(true, callback, scope); - } else { - root.collapseChildren(true, callback, scope); - } - Ext.resumeLayouts(true); - } - }, - /** - * Expand the tree to the path of a particular node. This is the way to expand a known path - * when the intervening nodes are not yet loaded. - * - * The path may be an absolute path (beginning with a `'/'` character) from the root, eg: - * - * '/rootId/nodeA/nodeB/nodeC' - * - * Or, the path may be relative, starting from an **existing** node in the tree: - * - * 'nodeC/nodeD' - * - * @param {String} path The path to expand. The path may be absolute, including a leading separator and starting - * from the root node id, or relative with no leading separator, starting from an *existing* - * node in the tree. - * @param {Object} [options] An object containing options to modify the operation. - * @param {String} [options.field] The field to get the data from. Defaults to the model idProperty. - * @param {String} [options.separator='/'] A separator to use. - * @param {Boolean} [options.select] Pass as `true` to select the specified row. - * @param {Boolean} [options.focus] Pass as `true` to focus the specified row. - * @param {Function} [options.callback] A function to execute when the expand finishes. - * @param {Boolean} options.callback.success `true` if the node expansion was successful. - * @param {Ext.data.Model} options.callback.record If successful, the target record. - * @param {HTMLElement} options.callback.node If successful, the record's view node. If unsuccessful, the - * last view node encountered while expanding the path. - * @param {Object} [options.scope] The scope (`this` reference) in which the callback function is executed. - */ - expandPath: function(path, options) { - var args = arguments, - me = this, - view = me.view, - field = (options && options.field) || me.store.model.idProperty, - select, doFocus, - separator = (options && options.separator) || '/', - callback, scope, current, index, keys, rooted, expander; - // New option object API - if (options && typeof options === 'object') { - field = options.field || me.store.model.idProperty; - separator = options.separator || '/'; - callback = options.callback; - scope = options.scope; - select = options.select; - doFocus = options.focus; - } else // Old multi argument API - { - field = args[1] || me.store.model.idProperty; - separator = args[2] || '/'; - callback = args[3]; - scope = args[4]; - } - if (Ext.isEmpty(path)) { - return Ext.callback(callback, scope || me, [ - false, - null - ]); - } - keys = path.split(separator); - // If they began the path with '/', this indicates starting from the root ID. - // otherwise, then can start at any *existing* node id. - rooted = !keys[0]; - if (rooted) { - current = me.getRootNode(); - index = 1; - } else // Not rooted, gather the first node in the path which MUST already exist. - { - current = me.store.findNode(field, keys[0]); - index = 0; - } - // Invalid root. Relative start could not be found, absolute start was not the rootNode. - if (!current || (rooted && current.get(field) !== keys[1])) { - return Ext.callback(callback, scope || me, [ - false, - current - ]); - } - // The expand success callback passed to every expand call down the path. - // Called in the scope of the node being expanded. - expander = function(newChildren) { - var node = this, - len, i, value; - // We've arrived at the end of the path. - if (++index === keys.length) { - if (select) { - view.getSelectionModel().select(node); - } - if (doFocus) { - view.getNavigationModel().setPosition(node, 0); - } - return Ext.callback(callback, scope || me, [ - true, - node, - view.getNode(node) - ]); - } - // Find the next child in the path if it's there and expand it. - for (i = 0 , len = newChildren ? newChildren.length : 0; i < len; i++) { - // The ids paths may be numeric, so cast the value to a string for comparison - node = newChildren[i]; - value = node.get(field); - if (value || value === 0) { - value = value.toString(); - } - if (value === keys[index]) { - return node.expand(false, expander); - } - } - // If we get here, there's been a miss along the path, and the operation is a fail. - node = this; - Ext.callback(callback, scope || me, [ - false, - node, - view.getNode(node) - ]); - }; - current.expand(false, expander); - }, - /** - * Expand the tree to the path of a particular node, then scroll it into view. - * @param {String} path The path to bring into view. The path may be absolute, including a leading separator and starting - * from the root node id, or relative with no leading separator, starting from an *existing* node in the tree. - * @param {Object} [options] An object containing options to modify the operation. - * @param {String} [options.field] The field to get the data from. Defaults to the model idProperty. - * @param {String} [options.separator='/'] A separator to use. - * @param {Boolean} [options.animate] Pass `true` to animate the row into view. - * @param {Boolean} [options.highlight] Pass `true` to highlight the row with a glow animation when it is in view. - * @param {Boolean} [options.select] Pass as `true` to select the specified row. - * @param {Boolean} [options.focus] Pass as `true` to focus the specified row. - * @param {Function} [options.callback] A function to execute when the expand finishes. - * @param {Boolean} options.callback.success `true` if the node expansion was successful. - * @param {Ext.data.Model} options.callback.record If successful, the target record. - * @param {HTMLElement} options.callback.node If successful, the record's view node. If unsuccessful, the - * last view node encountered while expanding the path. - * @param {Object} [options.scope] The scope (`this` reference) in which the callback function is executed. - */ - ensureVisible: function(path, options) { - // They passed a record instance or row index. Use the TablePanel's method. - if (path.isEntity || typeof path === 'number') { - return this.callParent([ - path, - options - ]); - } - var me = this, - field = (options && options.field) || me.store.model.idProperty, - separator = (options && options.separator) || '/', - callback, scope, keys, rooted, last, node, parentNode, - onLastExpanded = function(success, lastExpanded, lastExpandedHtmlNode, targetNode) { - if (!targetNode && success && lastExpanded) { - targetNode = lastExpanded.findChild(field, last); - } - // Once we have the node, we can use the TablePanel's ensureVisible method - if (targetNode) { - me.doEnsureVisible(targetNode, options); - } else { - Ext.callback(callback, scope || me, [ - false, - lastExpanded - ]); - } - }; - if (options) { - callback = options.callback; - scope = options.scope; - } - keys = path.split(separator); - rooted = !keys[0]; - last = keys.pop(); - // If the path was "foo/bar" or "/foo/Bar" - if (keys.length && !(rooted && keys.length === 1)) { - me.expandPath(keys.join(separator), field, separator, onLastExpanded); - } else // If the path was "foo" or "/foo" - { - node = me.store.findNode(field, last); - if (node) { - parentNode = node.parentNode; - if (parentNode && !parentNode.isExpanded()) { - parentNode.expand(); - } - // Pass the target node as the 4th parameter so the callback doesn't have to look it up - onLastExpanded(true, null, null, node); - } else { - Ext.callback(callback, scope || me, [ - false, - null - ]); - } - } - }, - /** - * Expand the tree to the path of a particular node, then select it. - * @param {String} path The path to expand. The path may be absolute, including a leading separator and - * starting from the root node id, or relative with no leading separator, starting from - * an *existing* node in the tree. - * @param {String} [field] The field to get the data from. Defaults to the model idProperty. - * @param {String} [separator='/'] A separator to use. - * @param {Function} [callback] A function to execute when the select finishes. - * @param {Boolean} callback.success `true` if the node expansion was successful. - * @param {Ext.data.NodeInterface} callback.lastNode If successful, the target node. If unsuccessful, the - * last tree node encountered while expanding the path. - * @param {HTMLElement} callback.node If successful, the record's view node. - * @param {Object} [scope] The scope of the callback function - */ - selectPath: function(path, field, separator, callback, scope) { - this.ensureVisible(path, { - field: field, - separator: separator, - select: true, - callback: callback, - scope: scope - }); - } -}); - -/** - * @private - */ -Ext.define('Ext.view.DragZone', { - extend: 'Ext.dd.DragZone', - containerScroll: false, - constructor: function(config) { - var me = this, - view, ownerCt, el; - Ext.apply(me, config); - // Create a ddGroup unless one has been configured. - // User configuration of ddGroups allows users to specify which - // DD instances can interact with each other. Using one - // based on the id of the View would isolate it and mean it can only - // interact with a DropZone on the same View also using a generated ID. - if (!me.ddGroup) { - me.ddGroup = 'view-dd-zone-' + me.view.id; - } - // Ext.dd.DragDrop instances are keyed by the ID of their encapsulating element. - // So a View's DragZone cannot use the View's main element because the DropZone must use that - // because the DropZone may need to scroll on hover at a scrolling boundary, and it is the View's - // main element which handles scrolling. - // We use the View's parent element to drag from. Ideally, we would use the internal structure, but that - // is transient; DataView's recreate the internal structure dynamically as data changes. - // TODO: Ext 5.0 DragDrop must allow multiple DD objects to share the same element. - view = me.view; - ownerCt = view.ownerCt; - // We don't just grab the parent el, since the parent el may be - // some el injected by the layout - if (ownerCt) { - el = ownerCt.getTargetEl().dom; - } else { - el = view.el.dom.parentNode; - } - me.callParent([ - el - ]); - me.ddel = document.createElement('div'); - me.ddel.className = Ext.baseCSSPrefix + 'grid-dd-wrap'; - }, - init: function(id, sGroup, config) { - var me = this, - eventSpec = { - itemmousedown: me.onItemMouseDown, - scope: me - }; - // If there may be ambiguity with touch/swipe to scroll and a drag gesture - // *also* trigger drag start on longpress - if (Ext.supports.touchScroll) { - eventSpec['itemlongpress'] = me.onItemMouseDown; - } - me.initTarget(id, sGroup, config); - me.view.mon(me.view, eventSpec); - }, - onValidDrop: function(target, e, id) { - this.callParent([ - target, - e, - id - ]); - // focus the view that the node was dropped onto so that keynav will be enabled. - target.el.focus(); - }, - onItemMouseDown: function(view, record, item, index, e) { - var navModel; - // Only respond to longpress for touch dragging. - // Reject drag start if mousedown is on the actionable cell of a grid view - if ((e.pointerType === 'touch' && e.type !== 'longpress') || (e.position && e.position.isEqual(e.view.actionPosition))) { - return; - } - if (!this.isPreventDrag(e, record, item, index)) { - navModel = view.getNavigationModel(); - // Since handleMouseDown prevents the default behavior of the event, which - // is to focus the view, we focus the view now. This ensures that the view - // remains focused if the drag is cancelled, or if no drag occurs. - // - // A Table event will have a position property which is a CellContext - if (e.position) { - navModel.setPosition(e.position); - } else // Otherwise, just use the item index - { - navModel.setPosition(index); - } - this.handleMouseDown(e); - } - }, - /** - * @protected - * Template method called upon mousedown. May be overridden in subclasses, or configured - * into an instance. - * - * Return `true` to prevent drag start. - * @param {Ext.event.Event} e The mousedown event. - * @param {Ext.data.Model} record The record mousedowned upon. - * @param {HTMLElement} item The grid row mousedowned upon. - * @param {Number} index The row number mousedowned upon. - */ - isPreventDrag: function(e, record, item, index) { - return false; - }, - getDragData: function(e) { - var view = this.view, - item = e.getTarget(view.getItemSelector()); - if (item) { - return { - copy: view.copy || (view.allowCopy && e.ctrlKey), - event: e, - view: view, - ddel: this.ddel, - item: item, - records: view.getSelectionModel().getSelection(), - fromPosition: Ext.fly(item).getXY() - }; - } - }, - onInitDrag: function(x, y) { - var me = this, - data = me.dragData, - view = data.view, - selectionModel = view.getSelectionModel(), - record = view.getRecord(data.item); - // Update the selection to match what would have been selected if the user had - // done a full click on the target node rather than starting a drag from it - if (!selectionModel.isSelected(record)) { - selectionModel.selectWithEvent(record, me.DDMInstance.mousedownEvent); - } - data.records = selectionModel.getSelection(); - Ext.fly(me.ddel).setHtml(me.getDragText()); - me.proxy.update(me.ddel); - me.onStartDrag(x, y); - return true; - }, - getDragText: function() { - var count = this.dragData.records.length; - return Ext.String.format(this.dragText, count, count === 1 ? '' : 's'); - }, - getRepairXY: function(e, data) { - return data ? data.fromPosition : false; - } -}); - -/** - * @private - */ -Ext.define('Ext.tree.ViewDragZone', { - extend: 'Ext.view.DragZone', - isPreventDrag: function(e, record) { - return (record.get('allowDrag') === false) || !!e.getTarget(this.view.expanderSelector); - }, - getDragText: function() { - var records = this.dragData.records, - count = records.length, - text = records[0].get(this.displayField), - suffix = 's', - formatRe = /\{\d+\}/, - dragText = this.dragText; - if (formatRe.test(dragText) && count === 1 && text) { - return text; - } else if (!text) { - suffix = ''; - } - return Ext.String.format(dragText, count, suffix); - }, - afterRepair: function() { - var me = this, - view = me.view, - selectedRowCls = view.selectedItemCls, - records = me.dragData.records, - r, - rLen = records.length, - fly = Ext.fly, - item; - if (Ext.enableFx && me.repairHighlight) { - // Roll through all records and highlight all the ones we attempted to drag. - for (r = 0; r < rLen; r++) { - // anonymous fns below, don't hoist up unless below is wrapped in - // a self-executing function passing in item. - item = view.getNode(records[r]); - // We must remove the selected row class before animating, because - // the selected row class declares !important on its background-color. - fly(item.firstChild).highlight(me.repairHighlightColor, { - listeners: { - beforeanimate: function() { - if (view.isSelected(item)) { - fly(item).removeCls(selectedRowCls); - } - }, - afteranimate: function() { - if (view.isSelected(item)) { - fly(item).addCls(selectedRowCls); - } - } - } - }); - } - } - me.dragging = false; - } -}); - -/** - * @private - */ -Ext.define('Ext.tree.ViewDropZone', { - extend: 'Ext.view.DropZone', - /** - * @cfg {Boolean} allowParentInserts - * Allow inserting a dragged node between an expanded parent node and its first child that will become a - * sibling of the parent when dropped. - */ - allowParentInserts: false, - /** - * @cfg {Boolean} allowContainerDrops - * True if drops on the tree container (outside of a specific tree node) are allowed. - * - * These are treated as appends to the root node. - */ - allowContainerDrops: false, - /** - * @cfg {Boolean} appendOnly - * True if the tree should only allow append drops (use for trees which are sorted). - */ - appendOnly: false, - /** - * @cfg {Number} expandDelay - * The delay in milliseconds to wait before expanding a target tree node while dragging a droppable node - * over the target. - */ - expandDelay: 500, - indicatorCls: Ext.baseCSSPrefix + 'tree-ddindicator', - /** - * @private - */ - expandNode: function(node) { - var view = this.view; - this.expandProcId = false; - if (!node.isLeaf() && !node.isExpanded()) { - view.expand(node); - this.expandProcId = false; - } - }, - /** - * @private - */ - queueExpand: function(node) { - this.expandProcId = Ext.Function.defer(this.expandNode, this.expandDelay, this, [ - node - ]); - }, - /** - * @private - */ - cancelExpand: function() { - if (this.expandProcId) { - clearTimeout(this.expandProcId); - this.expandProcId = false; - } - }, - getPosition: function(e, node) { - var view = this.view, - record = view.getRecord(node), - y = e.getY(), - noAppend = record.isLeaf(), - noBelow = false, - region = Ext.fly(node).getRegion(), - fragment; - // If we are dragging on top of the root node of the tree, we always want to append. - if (record.isRoot()) { - return 'append'; - } - // Return 'append' if the node we are dragging on top of is not a leaf else return false. - if (this.appendOnly) { - return noAppend ? false : 'append'; - } - if (!this.allowParentInserts) { - noBelow = record.hasChildNodes() && record.isExpanded(); - } - fragment = (region.bottom - region.top) / (noAppend ? 2 : 3); - if (y >= region.top && y < (region.top + fragment)) { - return 'before'; - } else if (!noBelow && (noAppend || (y >= (region.bottom - fragment) && y <= region.bottom))) { - return 'after'; - } else { - return 'append'; - } - }, - isValidDropPoint: function(node, position, dragZone, e, data) { - if (!node || !data.item) { - return false; - } - var view = this.view, - targetNode = view.getRecord(node), - draggedRecords = data.records, - dataLength = draggedRecords.length, - ln = draggedRecords.length, - i, record; - // No drop position, or dragged records: invalid drop point - if (!(targetNode && position && dataLength)) { - return false; - } - // If the targetNode is within the folder we are dragging - for (i = 0; i < ln; i++) { - record = draggedRecords[i]; - if (record.isNode && record.contains(targetNode)) { - return false; - } - } - // Respect the allowDrop field on Tree nodes - if (position === 'append' && targetNode.get('allowDrop') === false) { - return false; - } else if (position !== 'append' && targetNode.parentNode.get('allowDrop') === false) { - return false; - } - // If the target record is in the dragged dataset, then invalid drop - if (Ext.Array.contains(draggedRecords, targetNode)) { - return false; - } - return view.fireEvent('nodedragover', targetNode, position, data, e) !== false; - }, - onNodeOver: function(node, dragZone, e, data) { - var position = this.getPosition(e, node), - returnCls = this.dropNotAllowed, - view = this.view, - targetNode = view.getRecord(node), - indicator = this.getIndicator(), - indicatorY = 0; - // auto node expand check - this.cancelExpand(); - if (position === 'append' && !this.expandProcId && !Ext.Array.contains(data.records, targetNode) && !targetNode.isLeaf() && !targetNode.isExpanded()) { - this.queueExpand(targetNode); - } - if (this.isValidDropPoint(node, position, dragZone, e, data)) { - this.valid = true; - this.currentPosition = position; - this.overRecord = targetNode; - indicator.setWidth(Ext.fly(node).getWidth()); - indicatorY = Ext.fly(node).getY() - Ext.fly(view.el).getY() - 1; - // If view is scrolled using CSS translate, account for then when positioning the indicator - if (view.touchScroll === 2) { - indicatorY += view.getScrollY(); - } - /* - * In the code below we show the proxy again. The reason for doing this is showing the indicator will - * call toFront, causing it to get a new z-index which can sometimes push the proxy behind it. We always - * want the proxy to be above, so calling show on the proxy will call toFront and bring it forward. - */ - if (position === 'before') { - returnCls = targetNode.isFirst() ? Ext.baseCSSPrefix + 'tree-drop-ok-above' : Ext.baseCSSPrefix + 'tree-drop-ok-between'; - indicator.showAt(0, indicatorY); - dragZone.proxy.show(); - } else if (position === 'after') { - returnCls = targetNode.isLast() ? Ext.baseCSSPrefix + 'tree-drop-ok-below' : Ext.baseCSSPrefix + 'tree-drop-ok-between'; - indicatorY += Ext.fly(node).getHeight(); - indicator.showAt(0, indicatorY); - dragZone.proxy.show(); - } else { - returnCls = Ext.baseCSSPrefix + 'tree-drop-ok-append'; - // @TODO: set a class on the parent folder node to be able to style it - indicator.hide(); - } - } else { - this.valid = false; - } - this.currentCls = returnCls; - return returnCls; - }, - // The mouse is no longer over a tree node, so dropping is not valid - onNodeOut: function(n, dd, e, data) { - this.valid = false; - this.getIndicator().hide(); - }, - onContainerOver: function(dd, e, data) { - return this.allowContainerDrops ? this.dropAllowed : e.getTarget('.' + this.indicatorCls) ? this.currentCls : this.dropNotAllowed; - }, - // This will be called is allowContainerDrops is set. - // The target node is the root - onContainerDrop: function(dragZone, e, data) { - if (this.allowContainerDrops) { - this.valid = true; - this.currentPosition = 'append'; - this.overRecord = this.view.store.getRoot(); - this.onNodeDrop(this.overRecord, dragZone, e, data); - } - }, - notifyOut: function() { - this.callParent(arguments); - this.cancelExpand(); - }, - handleNodeDrop: function(data, targetNode, position) { - var me = this, - targetView = me.view, - parentNode = targetNode ? targetNode.parentNode : targetView.panel.getRootNode(), - Model = targetView.store.getModel(), - records, i, len, record, insertionMethod, argList, needTargetExpand, transferData; - // If the copy flag is set, create a copy of the models - if (data.copy) { - records = data.records; - data.records = []; - for (i = 0 , len = records.length; i < len; i++) { - record = records[i]; - if (record.isNode) { - data.records.push(record.copy()); - } else { - // If it's not a node, make a node copy - data.records.push(new Model(Ext.apply({}, record.data))); - } - } - } - // Cancel any pending expand operation - me.cancelExpand(); - // Grab a reference to the correct node insertion method. - // Create an arg list array intended for the apply method of the - // chosen node insertion method. - // Ensure the target object for the method is referenced by 'targetNode' - if (position === 'before') { - insertionMethod = parentNode.insertBefore; - argList = [ - null, - targetNode - ]; - targetNode = parentNode; - } else if (position === 'after') { - if (targetNode.nextSibling) { - insertionMethod = parentNode.insertBefore; - argList = [ - null, - targetNode.nextSibling - ]; - } else { - insertionMethod = parentNode.appendChild; - argList = [ - null - ]; - } - targetNode = parentNode; - } else { - if (!(targetNode.isExpanded() || targetNode.isLoading())) { - needTargetExpand = true; - } - insertionMethod = targetNode.appendChild; - argList = [ - null - ]; - } - // A function to transfer the data into the destination tree - transferData = function() { - var color, n; - // Coalesce layouts caused by node removal, appending and sorting - Ext.suspendLayouts(); - // Insert the records into the target node - for (i = 0 , len = data.records.length; i < len; i++) { - record = data.records[i]; - if (!record.isNode) { - if (record.isModel) { - record = new Model(record.data, record.getId()); - } else { - record = new Model(record); - } - data.records[i] = record; - } - argList[0] = record; - insertionMethod.apply(targetNode, argList); - } - // If configured to sort on drop, do it according to the TreeStore's comparator - if (me.sortOnDrop) { - targetNode.sort(targetNode.getOwnerTree().store.getSorters().sortFn); - } - Ext.resumeLayouts(true); - // Focus the dropped node. - record = data.records[0]; - targetView.ownerGrid.ensureVisible(record); - targetView.getNavigationModel().setPosition(record); - // Kick off highlights after everything's been inserted, so they are - // more in sync without insertion/render overhead. - // Element.highlight can handle highlighting table nodes. - if (Ext.enableFx && me.dropHighlight) { - color = me.dropHighlightColor; - for (i = 0; i < len; i++) { - n = targetView.getNode(data.records[i]); - if (n) { - Ext.fly(n).highlight(color); - } - } - } - }; - // If dropping right on an unexpanded node, transfer the data after it is expanded. - if (needTargetExpand) { - targetNode.expand(false, transferData); - } - // If the node is waiting for its children, we must transfer the data after the expansion. - // The expand event does NOT signal UI expansion, it is the SIGNAL for UI expansion. - // It's listened for by the NodeStore on the root node. Which means that listeners on the target - // node get notified BEFORE UI expansion. So we need a delay. - // TODO: Refactor NodeInterface.expand/collapse to notify its owning tree directly when it needs to expand/collapse. - else if (targetNode.isLoading()) { - targetNode.on({ - expand: transferData, - delay: 1, - single: true - }); - } else // Otherwise, call the data transfer function immediately - { - transferData(); - } - } -}); - -/** - * This plugin provides drag and drop functionality for a {@link Ext.tree.View TreeView}. - * - * A specialized instance of {@link Ext.dd.DragZone DragZone} and {@link Ext.dd.DropZone - * DropZone} are attached to the tree view. The DropZone will participate in drops - * from DragZones having the same {@link #ddGroup} including drops from within the same - * tree. - * - * During the drop operation a data object is passed to a participating DropZone's drop - * handlers. The drag data object has the following properties: - * - * - **copy:** {@link Boolean}
    The value of {@link #copy}. Or `true` if {@link - * #allowCopy} is true **and** the control key was pressed as the drag operation began. - * - * - **view:** {@link Ext.tree.View TreeView}
    The source tree view from which the - * drag originated - * - * - **ddel:** HTMLElement
    The drag proxy element which moves with the cursor - * - * - **item:** HTMLElement
    The tree view node upon which the mousedown event was - * registered - * - * - **records:** {@link Array}
    An Array of {@link Ext.data.Model Model}s - * representing the selected data being dragged from the source tree view. - * - * By adding this plugin to a view, two new events will be fired from the client - * tree view as well as its owning Tree: `{@link #beforedrop}` and `{@link #drop}`. - * - * var store = Ext.create('Ext.data.TreeStore', { - * root: { - * expanded: true, - * children: [{ - * text: "detention", - * leaf: true - * }, { - * text: "homework", - * expanded: true, - * children: [{ - * text: "book report", - * leaf: true - * }, { - * text: "algebra", - * leaf: true - * }] - * }, { - * text: "buy lottery tickets", - * leaf: true - * }] - * } - * }); - * - * Ext.create('Ext.tree.Panel', { - * title: 'Simple Tree', - * width: 200, - * height: 200, - * store: store, - * rootVisible: false, - * renderTo: document.body, - * viewConfig: { - * plugins: { - * ptype: 'treeviewdragdrop', - * dragText: 'Drag and drop to reorganize' - * } - * } - * }); - */ -Ext.define('Ext.tree.plugin.TreeViewDragDrop', { - extend: 'Ext.plugin.Abstract', - alias: 'plugin.treeviewdragdrop', - uses: [ - 'Ext.tree.ViewDragZone', - 'Ext.tree.ViewDropZone' - ], - /** - * @event beforedrop - * **This event is fired through the {@link Ext.tree.View TreeView} and its owning - * {@link Ext.tree.Panel Tree}. You can add listeners to the tree or tree {@link - * Ext.tree.Panel#viewConfig view config} object** - * - * Fired when a drop gesture has been triggered by a mouseup event in a valid drop - * position in the tree view. - * - * Returning `false` to this event signals that the drop gesture was invalid and - * animates the drag proxy back to the point from which the drag began. - * - * The dropHandlers parameter can be used to defer the processing of this event. For - * example, you can force the handler to wait for the result of a message box - * confirmation or an asynchronous server call (_see the details of the dropHandlers - * property for more information_). - * - * tree.on('beforedrop', function(node, data, overModel, dropPosition, dropHandlers) { - * // Defer the handling - * dropHandlers.wait = true; - * Ext.MessageBox.confirm('Drop', 'Are you sure', function(btn){ - * if (btn === 'yes') { - * dropHandlers.processDrop(); - * } else { - * dropHandlers.cancelDrop(); - * } - * }); - * }); - * - * Any other return value continues with the data transfer operation unless the wait - * property is set. - * - * @param {HTMLElement} node The {@link Ext.tree.View tree view} node **if any** over - * which the cursor was positioned. - * - * @param {Object} data The data object gathered at mousedown time by the - * cooperating {@link Ext.dd.DragZone DragZone}'s {@link Ext.dd.DragZone#getDragData - * getDragData} method. It contains the following properties: - * @param {Boolean} data.copy The value of {@link #copy}. Or `true` if - * {@link #allowCopy} is true **and** the control key was pressed as the drag - * operation began. - * @param {Ext.tree.View} data.view The source tree view from which the drag - * originated - * @param {HTMLElement} data.ddel The drag proxy element which moves with the cursor - * @param {HTMLElement} data.item The tree view node upon which the mousedown event - * was registered - * @param {Ext.data.TreeModel[]} data.records An Array of Models representing the - * selected data being dragged from the source tree view - * - * @param {Ext.data.TreeModel} overModel The Model over which the drop gesture took place - * - * @param {String} dropPosition `"before"` or `"after"` depending on whether the - * cursor is above or below the mid-line of the node. - * - * @param {Object} dropHandlers - * This parameter allows the developer to control when the drop action takes place. - * It is useful if any asynchronous processing needs to be completed before - * performing the drop. This object has the following properties: - * - * @param {Boolean} dropHandlers.wait Indicates whether the drop should be deferred. - * Set this property to true to defer the drop. - * @param {Function} dropHandlers.processDrop A function to be called to complete - * the drop operation. - * @param {Function} dropHandlers.cancelDrop A function to be called to cancel the - * drop operation. - */ - /** - * @event drop - * **This event is fired through the {@link Ext.tree.View TreeView} and its owning - * {@link Ext.tree.Panel Tree}. You can add listeners to the tree or tree {@link - * Ext.tree.Panel#viewConfig view config} object** - * - * Fired when a drop operation has been completed and the data has been moved or - * copied. - * - * @param {HTMLElement} node The {@link Ext.tree.View tree view} node **if any** over - * which the cursor was positioned. - * - * @param {Object} data The data object gathered at mousedown time by the - * cooperating {@link Ext.dd.DragZone DragZone}'s {@link Ext.dd.DragZone#getDragData - * getDragData} method. It contains the following properties: - * @param {Boolean} data.copy The value of {@link #copy}. Or `true` if - * {@link #allowCopy} is true **and** the control key was pressed as the drag - * operation began. - * @param {Ext.tree.View} data.view The source tree view from which the drag - * originated - * @param {HTMLElement} data.ddel The drag proxy element which moves with the cursor - * @param {HTMLElement} data.item The tree view node upon which the mousedown event - * was registered - * @param {Ext.data.TreeModel[]} data.records An Array of Models representing the - * selected data being dragged from the source tree view - * - * @param {Ext.data.TreeModel} overModel The Model over which the drop gesture took - * place. - * - * @param {String} dropPosition `"before"` or `"after"` depending on whether the - * cursor is above or below the mid-line of the node. - */ - /** - * @cfg {Boolean} [copy=false] - * Set as `true` to copy the records from the source grid to the destination drop - * grid. Otherwise, dragged records will be moved. - * - * **Note:** This only applies to records dragged between two different grids with - * unique stores. - * - * See {@link #allowCopy} to allow only control-drag operations to copy records. - */ - /** - * @cfg {Boolean} [allowCopy=false] - * Set as `true` to allow the user to hold down the control key at the start of the - * drag operation and copy the dragged records between grids. Otherwise, dragged - * records will be moved. - * - * **Note:** This only applies to records dragged between two different grids with - * unique stores. - * - * See {@link #copy} to enable the copying of all dragged records. - */ - // - /** - * @cfg - * The text to show while dragging. - * - * Two placeholders can be used in the text: - * - * - `{0}` The number of selected items. - * - `{1}` 's' when more than 1 items (only useful for English). - * - * **NOTE:** The node's {@link Ext.tree.Panel#cfg-displayField text} will be shown - * when a single node is dragged unless `dragText` is a simple text string. - */ - dragText: '{0} selected node{1}', - // - /** - * @cfg {Boolean} allowParentInserts - * Allow inserting a dragged node between an expanded parent node and its first child that will become a sibling of - * the parent when dropped. - */ - allowParentInserts: false, - /** - * @cfg {Boolean} allowContainerDrops - * True if drops on the tree container (outside of a specific tree node) are allowed. - */ - allowContainerDrops: false, - /** - * @cfg {Boolean} appendOnly - * True if the tree should only allow append drops (use for trees which are sorted). - */ - appendOnly: false, - /** - * @cfg {String} [ddGroup=TreeDD] - * A named drag drop group to which this object belongs. If a group is specified, then both the DragZones and - * DropZone used by this plugin will only interact with other drag drop objects in the same group. - */ - ddGroup: "TreeDD", - /** - * True to register this container with the Scrollmanager for auto scrolling during drag operations. - * A {@link Ext.dd.ScrollManager} configuration may also be passed. - * @cfg {Object/Boolean} containerScroll - */ - containerScroll: false, - /** - * @cfg {String} [dragGroup] - * The ddGroup to which the {@link #property-dragZone DragZone} will belong. - * - * This defines which other DropZones the DragZone will interact with. Drag/DropZones only interact with other - * Drag/DropZones which are members of the same ddGroup. - */ - /** - * @cfg {String} [dropGroup] - * The ddGroup to which the {@link #property-dropZone DropZone} will belong. - * - * This defines which other DragZones the DropZone will interact with. Drag/DropZones only interact with other - * Drag/DropZones which are members of the same {@link #ddGroup}. - */ - /** - * @cfg {Boolean} [sortOnDrop=false] - * Configure as `true` to sort the target node into the current tree sort order after the dropped node is added. - */ - /** - * @cfg {Number} [expandDelay=1000] - * The delay in milliseconds to wait before expanding a target tree node while dragging a droppable node over the - * target. - */ - expandDelay: 1000, - /** - * @cfg {Boolean} enableDrop - * Set to `false` to disallow the View from accepting drop gestures. - */ - enableDrop: true, - /** - * @cfg {Boolean} enableDrag - * Set to `false` to disallow dragging items from the View. - */ - enableDrag: true, - /** - * @cfg {String} [nodeHighlightColor=c3daf9] - * The color to use when visually highlighting the dragged or dropped node (default value is light blue). - * The color must be a 6 digit hex value, without a preceding '#'. See also {@link #nodeHighlightOnDrop} and - * {@link #nodeHighlightOnRepair}. - */ - nodeHighlightColor: 'c3daf9', - /** - * @cfg {Boolean} nodeHighlightOnDrop - * Whether or not to highlight any nodes after they are - * successfully dropped on their target. Defaults to the value of `Ext.enableFx`. - * See also {@link #nodeHighlightColor} and {@link #nodeHighlightOnRepair}. - */ - nodeHighlightOnDrop: Ext.enableFx, - /** - * @cfg {Boolean} nodeHighlightOnRepair - * Whether or not to highlight any nodes after they are - * repaired from an unsuccessful drag/drop. Defaults to the value of `Ext.enableFx`. - * See also {@link #nodeHighlightColor} and {@link #nodeHighlightOnDrop}. - */ - /** - * @cfg {String} [displayField=text] - * The name of the model field that is used to display the text for the nodes - */ - displayField: 'text', - /** - * @cfg {Object} [dragZone] - * A config object to apply to the creation of the {@link #property-dragZone DragZone} which handles for drag start gestures. - * - * Template methods of the DragZone may be overridden using this config. - */ - /** - * @cfg {Object} [dropZone] - * A config object to apply to the creation of the {@link #property-dropZone DropZone} which handles mouseover and drop gestures. - * - * Template methods of the DropZone may be overridden using this config. - */ - /** - * @property {Ext.view.DragZone} dragZone - * An {@link Ext.view.DragZone DragZone} which handles mousedown and dragging of records from the grid. - */ - /** - * @property {Ext.grid.ViewDropZone} dropZone - * An {@link Ext.grid.ViewDropZone DropZone} which handles mouseover and dropping records in any grid which shares the same {@link #dropGroup}. - */ - init: function(view) { - Ext.applyIf(view, { - copy: this.copy, - allowCopy: this.allowCopy - }); - view.on('render', this.onViewRender, this, { - single: true - }); - }, - destroy: function() { - var me = this; - me.dragZone = me.dropZone = Ext.destroy(me.dragZone, me.dropZone); - me.callParent(); - }, - onViewRender: function(view) { - var me = this, - ownerGrid = view.ownerCt.ownerGrid || view.ownerCt, - scrollEl; - ownerGrid.relayEvents(view, [ - 'beforedrop', - 'drop' - ]); - if (me.enableDrag) { - if (me.containerScroll) { - scrollEl = view.getEl(); - } - me.dragZone = new Ext.tree.ViewDragZone(Ext.apply({ - view: view, - ddGroup: me.dragGroup || me.ddGroup, - dragText: me.dragText, - displayField: me.displayField, - repairHighlightColor: me.nodeHighlightColor, - repairHighlight: me.nodeHighlightOnRepair, - scrollEl: scrollEl - }, me.dragZone)); - } - if (me.enableDrop) { - me.dropZone = new Ext.tree.ViewDropZone(Ext.apply({ - view: view, - ddGroup: me.dropGroup || me.ddGroup, - allowContainerDrops: me.allowContainerDrops, - appendOnly: me.appendOnly, - allowParentInserts: me.allowParentInserts, - expandDelay: me.expandDelay, - dropHighlightColor: me.nodeHighlightColor, - dropHighlight: me.nodeHighlightOnDrop, - sortOnDrop: me.sortOnDrop, - containerScroll: me.containerScroll - }, me.dropZone)); - } - } -}, function() { - var proto = this.prototype; - proto.nodeHighlightOnDrop = proto.nodeHighlightOnRepair = Ext.enableFx; -}); - -/** - * Utility class for manipulating CSS rules - * @singleton - */ -Ext.define('Ext.util.CSS', function() { - var CSS, - rules = null, - doc = document, - camelRe = /(-[a-z])/gi, - camelFn = function(m, a) { - return a.charAt(1).toUpperCase(); - }; - return { - singleton: true, - rules: rules, - initialized: false, - /** - * @private - */ - constructor: function() { - // Cache a reference to the singleton - CSS = this; - }, - /** - * Creates a stylesheet from a text blob of rules. - * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document. - * @param {String} cssText The text containing the css rules - * @param {String} id An id to add to the stylesheet for later removal - * @return {CSSStyleSheet} - */ - createStyleSheet: function(cssText, id) { - var ss, - head = doc.getElementsByTagName('head')[0], - styleEl = doc.createElement('style'); - styleEl.setAttribute('type', 'text/css'); - if (id) { - styleEl.setAttribute('id', id); - } - // Feature detect old IE - ss = styleEl.styleSheet; - if (ss) { - head.appendChild(styleEl); - ss.cssText = cssText; - } else { - styleEl.appendChild(doc.createTextNode(cssText)); - head.appendChild(styleEl); - ss = styleEl.sheet; - } - CSS.cacheStyleSheet(ss); - return ss; - }, - /** - * Removes a style or link tag by id - * @param {String} id The id of the tag - */ - removeStyleSheet: function(id) { - var existing = doc.getElementById(id); - if (existing) { - existing.parentNode.removeChild(existing); - } - }, - /** - * Dynamically swaps an existing stylesheet reference for a new one - * @param {String} id The id of an existing link tag to remove - * @param {String} url The href of the new stylesheet to include - */ - swapStyleSheet: function(id, url) { - var ss; - CSS.removeStyleSheet(id); - ss = doc.createElement("link"); - ss.setAttribute("rel", "stylesheet"); - ss.setAttribute("type", "text/css"); - ss.setAttribute("id", id); - ss.setAttribute("href", url); - doc.getElementsByTagName("head")[0].appendChild(ss); - }, - /** - * @private - */ - cacheStyleSheet: function(ss) { - if (!rules) { - rules = CSS.rules = {}; - } - try { - // try catch for cross domain access issue - var ssRules = ss.cssRules || ss.rules, - i = ssRules.length - 1, - imports = ss.imports, - len = imports ? imports.length : 0, - rule, j; - // Old IE has a different way of handling imports - for (j = 0; j < len; ++j) { - CSS.cacheStyleSheet(imports[j]); - } - for (; i >= 0; --i) { - rule = ssRules[i]; - // If it's an @import rule, import its stylesheet - if (rule.styleSheet) { - CSS.cacheStyleSheet(rule.styleSheet); - } - CSS.cacheRule(rule, ss); - } - } catch (e) {} - }, - cacheRule: function(cssRule, styleSheet) { - // If it's an @import rule, import its stylesheet - if (cssRule.styleSheet) { - return CSS.cacheStyleSheet(cssRule.styleSheet); - } - var selectorText = cssRule.selectorText, - selectorCount, j; - if (selectorText) { - // Split in case there are multiple, comma-delimited selectors - selectorText = selectorText.split(','); - selectorCount = selectorText.length; - for (j = 0; j < selectorCount; j++) { - // IE<8 does not keep a reference to parentStyleSheet in the rule, so we - // must cache an object like this until IE<8 is deprecated. - rules[Ext.String.trim(selectorText[j]).toLowerCase()] = { - parentStyleSheet: styleSheet, - cssRule: cssRule - }; - } - } - }, - /** - * Gets all css rules for the document - * @param {Boolean} refreshCache true to refresh the internal cache - * @return {Object} An object (hash) of rules indexed by selector - */ - getRules: function(refreshCache) { - var result = {}, - selector; - if (rules === null || refreshCache) { - CSS.refreshCache(); - } - for (selector in rules) { - result[selector] = rules[selector].cssRule; - } - return result; - }, - /** - * Refresh the rule cache if you have dynamically added stylesheets - * @return {Object} An object (hash) of rules indexed by selector - */ - refreshCache: function() { - var ds = doc.styleSheets, - i = 0, - len = ds.length; - rules = CSS.rules = {}; - for (; i < len; i++) { - try { - if (!ds[i].disabled) { - CSS.cacheStyleSheet(ds[i]); - } - } catch (e) {} - } - }, - /** - * Gets an an individual CSS rule by selector(s) - * @param {String/String[]} selector The CSS selector or an array of selectors to try. The first selector that is found is returned. - * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically - * @return {CSSStyleRule} The CSS rule or null if one is not found - */ - getRule: function(selector, refreshCache, rawCache) { - var i, result; - if (!rules || refreshCache) { - CSS.refreshCache(); - } - if (!Ext.isArray(selector)) { - result = rules[selector.toLowerCase()]; - if (result && !rawCache) { - result = result.cssRule; - } - return result || null; - } - for (i = 0; i < selector.length; i++) { - if (rules[selector[i]]) { - return rawCache ? rules[selector[i].toLowerCase()] : rules[selector[i].toLowerCase()].cssRule; - } - } - return null; - }, - /** - * Creates a rule. - * @param {CSSStyleSheet} styleSheet The StyleSheet to create the rule in as returned from {@link #createStyleSheet}. - * @param {String} selector The selector to target the rule. - * @param {String} property The cssText specification eg `"color:red;font-weight:bold;text-decoration:underline"` - * @return {CSSStyleRule} The created rule - */ - createRule: function(styleSheet, selector, cssText) { - var result, - ruleSet = styleSheet.cssRules || styleSheet.rules, - index = ruleSet.length; - if (styleSheet.insertRule) { - styleSheet.insertRule(selector + ' {' + cssText + '}', index); - } else { - styleSheet.addRule(selector, cssText || ' '); - } - CSS.cacheRule(result = ruleSet[index], styleSheet); - return result; - }, - /** - * Updates a rule property - * @param {String/String[]} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found. - * @param {String} property The css property or a cssText specification eg `"color:red;font-weight:bold;text-decoration:underline"` - * @param {String} value The new value for the property - * @return {Boolean} true If a rule was found and updated - */ - updateRule: function(selector, property, value) { - var rule, i, styles; - if (!Ext.isArray(selector)) { - rule = CSS.getRule(selector); - if (rule) { - // 2 arg form means cssText sent, so parse it and update each style - if (arguments.length === 2) { - styles = Ext.Element.parseStyles(property); - for (property in styles) { - rule.style[property.replace(camelRe, camelFn)] = styles[property]; - } - } else { - rule.style[property.replace(camelRe, camelFn)] = value; - } - return true; - } - } else { - for (i = 0; i < selector.length; i++) { - if (CSS.updateRule(selector[i], property, value)) { - return true; - } - } - } - return false; - }, - deleteRule: function(selector) { - var rule = CSS.getRule(selector, false, true), - styleSheet, index; - if (rule) { - styleSheet = rule.parentStyleSheet; - index = Ext.Array.indexOf(styleSheet.cssRules || styleSheet.rules, rule.cssRule); - if (styleSheet.deleteRule) { - styleSheet.deleteRule(index); - } else { - styleSheet.removeRule(index); - } - delete rules[selector]; - } - } - }; -}); - -/** - * Utility class for setting/reading values from browser cookies. - * Values can be written using the {@link #set} method. - * Values can be read using the {@link #get} method. - * A cookie can be invalidated on the client machine using the {@link #clear} method. - */ -Ext.define('Ext.util.Cookies', { - singleton: true, - /** - * Creates a cookie with the specified name and value. Additional settings for the cookie may be optionally specified - * (for example: expiration, access restriction, SSL). - * @param {String} name The name of the cookie to set. - * @param {Object} value The value to set for the cookie. - * @param {Object} [expires] Specify an expiration date the cookie is to persist until. Note that the specified Date - * object will be converted to Greenwich Mean Time (GMT). - * @param {String} [path] Setting a path on the cookie restricts access to pages that match that path. Defaults to all - * pages ('/'). - * @param {String} [domain] Setting a domain restricts access to pages on a given domain (typically used to allow - * cookie access across subdomains). For example, "sencha.com" will create a cookie that can be accessed from any - * subdomain of sencha.com, including www.sencha.com, support.sencha.com, etc. - * @param {Boolean} [secure] Specify true to indicate that the cookie should only be accessible via SSL on a page - * using the HTTPS protocol. Defaults to false. Note that this will only work if the page calling this code uses the - * HTTPS protocol, otherwise the cookie will be created with default options. - */ - set: function(name, value) { - var argv = arguments, - argc = arguments.length, - expires = (argc > 2) ? argv[2] : null, - path = (argc > 3) ? argv[3] : '/', - domain = (argc > 4) ? argv[4] : null, - secure = (argc > 5) ? argv[5] : false; - document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toUTCString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : ""); - }, - /** - * Retrieves cookies that are accessible by the current page. If a cookie does not exist, `get()` returns null. The - * following example retrieves the cookie called "valid" and stores the String value in the variable validStatus. - * - * var validStatus = Ext.util.Cookies.get("valid"); - * - * @param {String} name The name of the cookie to get - * @return {Object} Returns the cookie value for the specified name; - * null if the cookie name does not exist. - */ - get: function(name) { - var parts = document.cookie.split('; '), - len = parts.length, - item, i, ret; - // In modern browsers, a cookie with an empty string will be stored: - // MyName= - // In older versions of IE, it will be stored as: - // MyName - // So here we iterate over all the parts in an attempt to match the key. - for (i = 0; i < len; ++i) { - item = parts[i].split('='); - if (item[0] === name) { - ret = item[1]; - return ret ? unescape(ret) : ''; - } - } - return null; - }, - /** - * Removes a cookie with the provided name from the browser - * if found by setting its expiration date to sometime in the past. - * @param {String} name The name of the cookie to remove - * @param {String} [path] The path for the cookie. - * This must be included if you included a path while setting the cookie. - */ - clear: function(name, path) { - if (this.get(name)) { - path = path || '/'; - document.cookie = name + '=' + '; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=' + path; - } - } -}); - -/** - * This component provides a grid holding selected items from a second store of potential - * members. The `store` of this component represents the selected items. The `searchStore` - * represents the potentially selected items. - * - * The default view defined by this class is intended to be easily replaced by deriving a - * new class and overriding the appropriate methods. For example, the following is a very - * different view that uses a date range and a data view: - * - * Ext.define('App.view.DateBoundSearch', { - * extend: 'Ext.view.MultiSelectorSearch', - * - * makeDockedItems: function () { - * return { - * xtype: 'toolbar', - * items: [{ - * xtype: 'datefield', - * emptyText: 'Start date...', - * flex: 1 - * },{ - * xtype: 'datefield', - * emptyText: 'End date...', - * flex: 1 - * }] - * }; - * }, - * - * makeItems: function () { - * return [{ - * xtype: 'dataview', - * itemSelector: '.search-item', - * selModel: 'rowselection', - * store: this.store, - * scrollable: true, - * tpl: - * '' + - * '
    ' + - * '' + - * '
    {name}
    ' + - * '
    ' + - * '
    ' - * }]; - * }, - * - * getSearchStore: function () { - * return this.items.getAt(0).getStore(); - * }, - * - * selectRecords: function (records) { - * var view = this.items.getAt(0); - * return view.getSelectionModel().select(records); - * } - * }); - * - * **Important**: This class assumes there are two components with specific `reference` - * names assigned to them. These are `"searchField"` and `"searchGrid"`. These components - * are produced by the `makeDockedItems` and `makeItems` method, respectively. When - * overriding these it is important to remember to place these `reference` values on the - * appropriate components. - */ -Ext.define('Ext.view.MultiSelectorSearch', { - extend: 'Ext.panel.Panel', - xtype: 'multiselector-search', - layout: 'fit', - floating: true, - resizable: true, - minWidth: 200, - minHeight: 200, - border: true, - defaultListenerScope: true, - referenceHolder: true, - /** - * @cfg {String} field - * A field from your grid's store that will be used for filtering your search results. - */ - /** - * @cfg store - * @inheritdoc Ext.panel.Table#store - */ - /** - * @cfg {String} searchText - * This text is displayed as the "emptyText" of the search `textfield`. - */ - searchText: 'Search...', - initComponent: function() { - var me = this, - owner = me.owner, - items = me.makeItems(), - i, item, records, store; - me.dockedItems = me.makeDockedItems(); - me.items = items; - store = Ext.data.StoreManager.lookup(me.store); - for (i = items.length; i--; ) { - if ((item = items[i]).xtype === 'grid') { - item.store = store; - item.isSearchGrid = true; - item.selModel = item.selModel || { - type: 'checkboxmodel', - pruneRemoved: false, - listeners: { - selectionchange: 'onSelectionChange' - } - }; - Ext.merge(item, me.grid); - if (!item.columns) { - item.hideHeaders = true; - item.columns = [ - { - flex: 1, - dataIndex: me.field - } - ]; - } - break; - } - } - me.callParent(); - records = me.getOwnerStore().getRange(); - if (!owner.convertSelectionRecord.$nullFn) { - for (i = records.length; i--; ) { - records[i] = owner.convertSelectionRecord(records[i]); - } - } - if (store.isLoading() || (store.loadCount === 0 && !store.getCount())) { - // If it is NOT a preloaded store, then unless a Session is being used, - // The newly loaded records will NOT match any in the ownerStore. - // So we must match them by ID in order to select the same dataset. - store.on('load', function() { - var len = records.length, - i, record, - toSelect = []; - if (!me.destroyed) { - for (i = 0; i < len; i++) { - record = store.getById(records[i].getId()); - if (record) { - toSelect.push(record); - } - } - me.selectRecords(toSelect); - } - }, null, { - single: true - }); - } else { - me.selectRecords(records); - } - }, - getOwnerStore: function() { - return this.owner.getStore(); - }, - afterShow: function() { - var searchField = this.lookupReference('searchField'); - this.callParent(arguments); - if (searchField) { - searchField.focus(); - } - }, - /** - * Returns the store that holds search results. By default this comes from the - * "search grid". If this aspect of the view is changed sufficiently so that the - * search grid cannot be found, this method should be overridden to return the proper - * store. - * @return {Ext.data.Store} - */ - getSearchStore: function() { - var searchGrid = this.lookupReference('searchGrid'); - return searchGrid.getStore(); - }, - makeDockedItems: function() { - return [ - { - xtype: 'textfield', - reference: 'searchField', - dock: 'top', - hideFieldLabel: true, - emptyText: this.searchText, - triggers: { - clear: { - cls: Ext.baseCSSPrefix + 'form-clear-trigger', - handler: 'onClearSearch', - hidden: true - } - }, - listeners: { - change: 'onSearchChange', - buffer: 300 - } - } - ]; - }, - makeItems: function() { - return [ - { - xtype: 'grid', - reference: 'searchGrid', - trailingBufferZone: 2, - leadingBufferZone: 2, - viewConfig: { - deferEmptyText: false, - emptyText: 'No results.' - } - } - ]; - }, - selectRecords: function(records) { - var searchGrid = this.lookupReference('searchGrid'); - return searchGrid.getSelectionModel().select(records); - }, - deselectRecords: function(records) { - var searchGrid = this.lookupReference('searchGrid'); - return searchGrid.getSelectionModel().deselect(records); - }, - search: function(text) { - var me = this, - filter = me.searchFilter, - filters = me.getSearchStore().getFilters(); - if (text) { - filters.beginUpdate(); - if (filter) { - filter.setValue(text); - } else { - me.searchFilter = filter = new Ext.util.Filter({ - id: 'search', - property: me.field, - value: text - }); - } - filters.add(filter); - filters.endUpdate(); - } else if (filter) { - filters.remove(filter); - } - }, - privates: { - onClearSearch: function() { - var searchField = this.lookupReference('searchField'); - searchField.setValue(null); - searchField.focus(); - }, - onSearchChange: function(searchField) { - var value = searchField.getValue(), - trigger = searchField.getTrigger('clear'); - trigger.setHidden(!value); - this.search(value); - }, - onSelectionChange: function(selModel, selection) { - var owner = this.owner, - store = owner.getStore(), - data = store.data, - remove = 0, - map = {}, - add, i, id, record; - for (i = selection.length; i--; ) { - record = selection[i]; - id = record.id; - map[id] = record; - if (!data.containsKey(id)) { - (add || (add = [])).push(owner.convertSearchRecord(record)); - } - } - for (i = data.length; i--; ) { - record = data.getAt(i); - if (!map[record.id]) { - (remove || (remove = [])).push(record); - } - } - if (add || remove) { - data.splice(data.length, remove, add); - } - } - } -}); - -/** - * This component provides a grid holding selected items from a second store of potential - * members. The `store` of this component represents the selected items. The "search store" - * represents the potentially selected items. - * - * While this component is a grid and so you can configure `columns`, it is best to leave - * that to this class in its `initComponent` method. That allows this class to create the - * extra column that allows the user to remove rows. Instead use `{@link #fieldName}` and - * `{@link #fieldTitle}` to configure the primary column's `dataIndex` and column `text`, - * respectively. - * - * @since 5.0.0 - */ -Ext.define('Ext.view.MultiSelector', { - extend: 'Ext.grid.Panel', - xtype: 'multiselector', - config: { - /** - * @cfg {Object} search - * This object configures the search popup component. By default this contains the - * `xtype` for a `Ext.view.MultiSelectorSearch` component and specifies `autoLoad` - * for its `store`. - */ - search: { - xtype: 'multiselector-search', - width: 200, - height: 200, - store: { - autoLoad: true - } - } - }, - /** - * @cfg {String} [fieldName="name"] - * The name of the data field to display in the primary column of the grid. - * @since 5.0.0 - */ - fieldName: 'name', - /** - * @cfg {String} [fieldTitle] - * The text to display in the column header for the primary column of the grid. - * @since 5.0.0 - */ - fieldTitle: null, - /** - * @cfg {String} removeRowText - * The text to display in the "remove this row" column. By default this is a Unicode - * "X" looking glyph. - * @since 5.0.0 - */ - removeRowText: '✖', - /** - * @cfg {String} removeRowTip - * The tooltip to display when the user hovers over the remove cell. - * @since 5.0.0 - */ - removeRowTip: 'Remove this item', - emptyText: 'Nothing selected', - /** - * @cfg {String} addToolText - * The tooltip to display when the user hovers over the "+" tool in the panel header. - * @since 5.0.0 - */ - addToolText: 'Search for items to add', - initComponent: function() { - var me = this, - emptyText = me.emptyText, - store = me.getStore(), - search = me.getSearch(), - fieldTitle = me.fieldTitle, - searchStore, model; - if (!search) { - Ext.raise('The search configuration is required for the multi selector'); - } - searchStore = search.store; - if (searchStore.isStore) { - model = searchStore.getModel(); - } else { - model = searchStore.model; - } - if (!store) { - me.store = { - model: model - }; - } - if (emptyText && !me.viewConfig) { - me.viewConfig = { - deferEmptyText: false, - emptyText: emptyText - }; - } - if (!me.columns) { - me.hideHeaders = !fieldTitle; - me.columns = [ - { - text: fieldTitle, - dataIndex: me.fieldName, - flex: 1 - }, - me.makeRemoveRowColumn() - ]; - } - me.callParent(); - }, - addTools: function() { - this.addTool({ - type: 'plus', - tooltip: this.addToolText, - callback: 'onShowSearch', - scope: this - }); - }, - convertSearchRecord: Ext.identityFn, - convertSelectionRecord: Ext.identityFn, - makeRemoveRowColumn: function() { - var me = this; - return { - width: 22, - menuDisabled: true, - tdCls: Ext.baseCSSPrefix + 'multiselector-remove', - processEvent: me.processRowEvent.bind(me), - renderer: me.renderRemoveRow, - updater: Ext.emptyFn, - scope: me - }; - }, - processRowEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) { - if (e.type !== 'click') { - return; - } - if (Ext.fly(cell).hasCls(Ext.baseCSSPrefix + 'multiselector-remove')) { - this.store.remove(record); - if (this.searchPopup) { - this.searchPopup.deselectRecords(record); - } - } - }, - renderRemoveRow: function() { - return '' + this.removeRowText + ''; - }, - beforeDestroy: function() { - Ext.un({ - mousedown: 'onDismissSearch', - scope: this - }); - this.callParent(); - }, - privates: { - onDismissSearch: function(e) { - var searchPopup = this.searchPopup; - if (searchPopup && !(searchPopup.owns(e.getTarget()) || this.owns(e.getTarget()))) { - Ext.un({ - mousedown: 'onDismissSearch', - scope: this - }); - searchPopup.hide(); - } - }, - onShowSearch: function(panel, tool) { - var me = this, - searchPopup = me.searchPopup, - store = me.getStore(); - if (!searchPopup) { - searchPopup = Ext.merge({ - owner: me, - field: me.fieldName, - floating: true - }, me.getSearch()); - me.searchPopup = searchPopup = me.add(searchPopup); - // If we were configured with records prior to the UI requesting the popup, - // ensure that the records are selected in the popup. - if (store.getCount()) { - searchPopup.selectRecords(store.getRange()); - } - } - searchPopup.showBy(me, 'tl-tr?'); - Ext.on({ - mousedown: 'onDismissSearch', - scope: me - }); - } - } -}); - -/* - * This class is a derived work from: - * - * Notification extension for Ext JS 4.0.2+ - * Version: 2.1.3 - * - * Copyright (c) 2011 Eirik Lorentsen (http://www.eirik.net/) - * - * Follow project on GitHub: https://github.com/EirikLorentsen/Ext.ux.window.Notification - * - * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) - * and GPL (http://opensource.org/licenses/GPL-3.0) licenses. - */ -/** - * This class provides for lightweight, auto-dismissing pop-up notifications called "toasts". - * At the base level, you can display a toast message by calling `Ext.toast` like so: - * - * Ext.toast('Data saved'); - * - * This will result in a toast message, which displays in the default location of bottom right in your viewport. - * - * You may expand upon this simple example with the following parameters: - * - * Ext.toast(message, title, align, iconCls); - * - * For example, the following toast will appear top-middle in your viewport. It will display - * the 'Data Saved' message with a title of 'Title' - * - * Ext.toast('Data Saved', 'Title', 't') - * - * It should be noted that the toast's width is determined by the message's width. - * If you need to set a specific width, or any of the other available configurations for your toast, - * you can create the toast object as seen below: - * - * Ext.toast({ - * html: 'Data Saved', - * title: 'My Title', - * width: 200, - * align: 't' - * }); - * - * This component is derived from the excellent work of a Sencha community member, Eirik - * Lorentsen. - */ -Ext.define('Ext.window.Toast', { - extend: 'Ext.window.Window', - xtype: 'toast', - isToast: true, - cls: Ext.baseCSSPrefix + 'toast', - bodyPadding: 10, - autoClose: true, - plain: false, - draggable: false, - resizable: false, - shadow: false, - focus: Ext.emptyFn, - /** - * @cfg {String/Ext.Component} [anchor] - * The component or the `id` of the component to which the `toast` will be anchored. - * The default behavior is to anchor a `toast` to the document body (no component). - */ - anchor: null, - /** - * @cfg {Boolean} [useXAxis] - * Directs the toast message to animate on the x-axis (if `true`) or y-axis (if `false`). - * This value defaults to a value based on the `align` config. - */ - useXAxis: false, - /** - * @cfg {"br"/"bl"/"tr"/"tl"/"t"/"l"/"b"/"r"} [align="br"] - * Specifies the basic alignment of the toast message with its {@link #anchor}. This - * controls many aspects of the toast animation as well. For fine grain control of - * the final placement of the toast and its `anchor` you may set - * {@link #anchorAlign} as well. - * - * Possible values: - * - * - br - bottom-right - * - bl - bottom-left - * - tr - top-right - * - tl - top-left - * - t - top - * - l - left - * - b - bottom - * - r - right - */ - align: 'br', - /** - * @cfg {String} [anchorAlign] - * This string is a full specification of how to position the toast with respect to - * its `anchor`. This is set to a reasonable value based on `align` but the `align` - * also sets defaults for various other properties. This config controls only the - * final position of the toast. - */ - /** - * @cfg {Boolean} [animate=true] - * Set this to `false` to make toasts appear and disappear without animation. - * This is helpful with applications' unit and integration testing. - */ - animate: true, - // Pixels between each notification - spacing: 6, - //TODO There should be a way to control from and to positions for the introduction. - //TODO The align/anchorAlign configs don't actually work as expected. - // Pixels from the anchor's borders to start the first notification - paddingX: 30, - paddingY: 10, - slideInAnimation: 'easeIn', - slideBackAnimation: 'bounceOut', - slideInDuration: 500, - slideBackDuration: 500, - hideDuration: 500, - autoCloseDelay: 3000, - stickOnClick: true, - stickWhileHover: true, - closeOnMouseDown: false, - closable: false, - // Private. Do not override! - isHiding: false, - isFading: false, - destroyAfterHide: false, - closeOnMouseOut: false, - // Caching coordinates to be able to align to final position of siblings being animated - xPos: 0, - yPos: 0, - initComponent: function() { - var me = this; - // Close tool is not really helpful to sight impaired users - // when Toast window is set to auto-close on timeout; however - // if it's forced, respect that. - if (me.autoClose && !me.hasOwnProperty('closable')) { - me.closable = false; - } - me.updateAlignment(me.align); - me.setAnchor(me.anchor); - me.callParent(); - }, - onRender: function() { - var me = this; - me.callParent(arguments); - me.el.hover(me.onMouseEnter, me.onMouseLeave, me); - // Mousedown outside of this, when visible, hides it immediately - if (me.closeOnMouseDown) { - Ext.getDoc().on('mousedown', me.onDocumentMousedown, me); - } - }, - /* - * These properties are keyed by "align" and set defaults for various configs. - */ - alignmentProps: { - br: { - paddingFactorX: -1, - paddingFactorY: -1, - siblingAlignment: "br-br", - anchorAlign: "tr-br" - }, - bl: { - paddingFactorX: 1, - paddingFactorY: -1, - siblingAlignment: "bl-bl", - anchorAlign: "tl-bl" - }, - tr: { - paddingFactorX: -1, - paddingFactorY: 1, - siblingAlignment: "tr-tr", - anchorAlign: "br-tr" - }, - tl: { - paddingFactorX: 1, - paddingFactorY: 1, - siblingAlignment: "tl-tl", - anchorAlign: "bl-tl" - }, - b: { - paddingFactorX: 0, - paddingFactorY: -1, - siblingAlignment: "b-b", - useXAxis: 0, - anchorAlign: "t-b" - }, - t: { - paddingFactorX: 0, - paddingFactorY: 1, - siblingAlignment: "t-t", - useXAxis: 0, - anchorAlign: "b-t" - }, - l: { - paddingFactorX: 1, - paddingFactorY: 0, - siblingAlignment: "l-l", - useXAxis: 1, - anchorAlign: "r-l" - }, - r: { - paddingFactorX: -1, - paddingFactorY: 0, - siblingAlignment: "r-r", - useXAxis: 1, - anchorAlign: "l-r" - }, - /* - * These properties take priority over the above and applied only when useXAxis - * is set to true. Again these are keyed by "align". - */ - x: { - br: { - anchorAlign: "bl-br" - }, - bl: { - anchorAlign: "br-bl" - }, - tr: { - anchorAlign: "tl-tr" - }, - tl: { - anchorAlign: "tr-tl" - } - } - }, - updateAlignment: function(align) { - var me = this, - alignmentProps = me.alignmentProps, - props = alignmentProps[align], - xprops = alignmentProps.x[align]; - if (xprops && me.useXAxis) { - Ext.applyIf(me, xprops); - } - Ext.applyIf(me, props); - }, - getXposAlignedToAnchor: function() { - var me = this, - align = me.align, - anchor = me.anchor, - anchorEl = anchor && anchor.el, - el = me.el, - xPos = 0; - // Avoid error messages if the anchor does not have a dom element - if (anchorEl && anchorEl.dom) { - if (!me.useXAxis) { - // Element should already be aligned vertically - xPos = el.getLeft(); - } - // Using getAnchorXY instead of getTop/getBottom should give a correct placement when document is used - // as the anchor but is still 0 px high. Before rendering the viewport. - else if (align === 'br' || align === 'tr' || align === 'r') { - xPos += anchorEl.getAnchorXY('r')[0]; - xPos -= (el.getWidth() + me.paddingX); - } else { - xPos += anchorEl.getAnchorXY('l')[0]; - xPos += me.paddingX; - } - } - return xPos; - }, - getYposAlignedToAnchor: function() { - var me = this, - align = me.align, - anchor = me.anchor, - anchorEl = anchor && anchor.el, - el = me.el, - yPos = 0; - // Avoid error messages if the anchor does not have a dom element - if (anchorEl && anchorEl.dom) { - if (me.useXAxis) { - // Element should already be aligned horizontally - yPos = el.getTop(); - } - // Using getAnchorXY instead of getTop/getBottom should give a correct placement when document is used - // as the anchor but is still 0 px high. Before rendering the viewport. - else if (align === 'br' || align === 'bl' || align === 'b') { - yPos += anchorEl.getAnchorXY('b')[1]; - yPos -= (el.getHeight() + me.paddingY); - } else { - yPos += anchorEl.getAnchorXY('t')[1]; - yPos += me.paddingY; - } - } - return yPos; - }, - getXposAlignedToSibling: function(sibling) { - var me = this, - align = me.align, - el = me.el, - xPos; - if (!me.useXAxis) { - xPos = el.getLeft(); - } else if (align === 'tl' || align === 'bl' || align === 'l') { - // Using sibling's width when adding - xPos = (sibling.xPos + sibling.el.getWidth() + sibling.spacing); - } else { - // Using own width when subtracting - xPos = (sibling.xPos - el.getWidth() - me.spacing); - } - return xPos; - }, - getYposAlignedToSibling: function(sibling) { - var me = this, - align = me.align, - el = me.el, - yPos; - if (me.useXAxis) { - yPos = el.getTop(); - } else if (align === 'tr' || align === 'tl' || align === 't') { - // Using sibling's width when adding - yPos = (sibling.yPos + sibling.el.getHeight() + sibling.spacing); - } else { - // Using own width when subtracting - yPos = (sibling.yPos - el.getHeight() - sibling.spacing); - } - return yPos; - }, - getToasts: function() { - var anchor = this.anchor, - alignment = this.anchorAlign, - activeToasts = anchor.activeToasts || (anchor.activeToasts = {}); - return activeToasts[alignment] || (activeToasts[alignment] = []); - }, - setAnchor: function(anchor) { - var me = this, - Toast; - me.anchor = anchor = ((typeof anchor === 'string') ? Ext.getCmp(anchor) : anchor); - // If no anchor is provided or found, then the static object is used and the el - // property pointed to the body document. - if (!anchor) { - Toast = Ext.window.Toast; - me.anchor = Toast.bodyAnchor || (Toast.bodyAnchor = { - el: Ext.getBody() - }); - } - }, - beforeShow: function() { - var me = this; - if (me.stickOnClick) { - me.body.on('click', function() { - me.cancelAutoClose(); - }); - } - if (me.autoClose) { - if (!me.closeTask) { - me.closeTask = new Ext.util.DelayedTask(me.doAutoClose, me); - } - } - // Shunting offscreen to avoid flicker - me.el.setX(-10000); - me.el.setOpacity(1); - }, - afterShow: function() { - var me = this, - el = me.el, - activeToasts, sibling, length, xy; - me.callParent(arguments); - activeToasts = me.getToasts(); - length = activeToasts.length; - sibling = length && activeToasts[length - 1]; - if (sibling) { - el.alignTo(sibling.el, me.siblingAlignment, [ - 0, - 0 - ]); - me.xPos = me.getXposAlignedToSibling(sibling); - me.yPos = me.getYposAlignedToSibling(sibling); - } else { - el.alignTo(me.anchor.el, me.anchorAlign, [ - (me.paddingX * me.paddingFactorX), - (me.paddingY * me.paddingFactorY) - ], false); - me.xPos = me.getXposAlignedToAnchor(); - me.yPos = me.getYposAlignedToAnchor(); - } - Ext.Array.include(activeToasts, me); - if (me.animate) { - // Repeating from coordinates makes sure the windows does not flicker - // into the center of the viewport during animation - xy = el.getXY(); - el.animate({ - from: { - x: xy[0], - y: xy[1] - }, - to: { - x: me.xPos, - y: me.yPos, - opacity: 1 - }, - easing: me.slideInAnimation, - duration: me.slideInDuration, - dynamic: true, - callback: me.afterPositioned, - scope: me - }); - } else { - me.setLocalXY(me.xPos, me.yPos); - me.afterPositioned(); - } - }, - afterPositioned: function() { - if (this.autoClose) { - this.closeTask.delay(this.autoCloseDelay); - } - }, - onDocumentMousedown: function(e) { - if (this.isVisible() && !this.owns(e.getTarget())) { - this.hide(); - } - }, - slideBack: function() { - var me = this, - anchor = me.anchor, - anchorEl = anchor && anchor.el, - el = me.el, - activeToasts = me.getToasts(), - index = Ext.Array.indexOf(activeToasts, me); - // Not animating the element if it already started to hide itself or if the anchor is not present in the dom - if (!me.isHiding && el && el.dom && anchorEl && anchorEl.isVisible()) { - if (index) { - me.xPos = me.getXposAlignedToSibling(activeToasts[index - 1]); - me.yPos = me.getYposAlignedToSibling(activeToasts[index - 1]); - } else { - me.xPos = me.getXposAlignedToAnchor(); - me.yPos = me.getYposAlignedToAnchor(); - } - me.stopAnimation(); - if (me.animate) { - el.animate({ - to: { - x: me.xPos, - y: me.yPos - }, - easing: me.slideBackAnimation, - duration: me.slideBackDuration, - dynamic: true - }); - } - } - }, - update: function() { - var me = this; - if (me.isVisible()) { - me.isHiding = true; - me.hide(); - } - //TODO offer a way to just update and reposition after layout - me.callParent(arguments); - me.show(); - }, - cancelAutoClose: function() { - var closeTask = this.closeTask; - if (closeTask) { - closeTask.cancel(); - } - }, - doAutoClose: function() { - var me = this; - if (!(me.stickWhileHover && me.mouseIsOver)) { - // Close immediately - me.close(); - } else { - // Delayed closing when mouse leaves the component. - me.closeOnMouseOut = true; - } - }, - onMouseEnter: function() { - this.mouseIsOver = true; - }, - onMouseLeave: function() { - var me = this; - me.mouseIsOver = false; - if (me.closeOnMouseOut) { - me.closeOnMouseOut = false; - me.close(); - } - }, - removeFromAnchor: function() { - var me = this, - activeToasts, index; - if (me.anchor) { - activeToasts = me.getToasts(); - index = Ext.Array.indexOf(activeToasts, me); - if (index !== -1) { - Ext.Array.erase(activeToasts, index, 1); - // Slide "down" all activeToasts "above" the hidden one - for (; index < activeToasts.length; index++) { - activeToasts[index].slideBack(); - } - } - } - }, - getFocusEl: Ext.emptyFn, - hide: function() { - var me = this, - el = me.el; - me.cancelAutoClose(); - if (me.isHiding) { - if (!me.isFading) { - me.callParent(arguments); - // Must come after callParent() since it will pass through hide() again triggered by destroy() - me.removeFromAnchor(); - me.isHiding = false; - } - } else { - // Must be set right away in case of double clicks on the close button - me.isHiding = true; - me.isFading = true; - me.cancelAutoClose(); - if (el) { - if (me.animate) { - el.fadeOut({ - opacity: 0, - easing: 'easeIn', - duration: me.hideDuration, - listeners: { - afteranimate: function() { - me.isFading = false; - me.hide(me.animateTarget, me.doClose, me); - } - } - }); - } else { - me.isFading = false; - me.hide(me.animateTarget, me.doClose, me); - } - } - } - return me; - } -}, function(Toast) { - Ext.toast = function(message, title, align, iconCls) { - var config = message, - toast; - if (Ext.isString(message)) { - config = { - title: title, - html: message, - iconCls: iconCls - }; - if (align) { - config.align = align; - } - } - toast = new Toast(config); - toast.show(); - return toast; - }; -}); - diff --git a/src/webapp/js/ext-all.js b/src/webapp/js/ext-all.js deleted file mode 100644 index cfcbc67..0000000 --- a/src/webapp/js/ext-all.js +++ /dev/null @@ -1,22 +0,0 @@ -/* -This file is part of Ext JS 6.0.1.250 - -Copyright (c) 2011-2015 Sencha Inc - -Contact: http://www.sencha.com/contact - -GNU General Public License Usage -This file may be used under the terms of the GNU General Public License version 3.0 as -published by the Free Software Foundation and appearing in the file LICENSE included in the -packaging of this file. - -Please review the following information to ensure the GNU General Public License version 3.0 -requirements will be met: http://www.gnu.org/copyleft/gpl.html. - -If you are unsure which license is appropriate for your use, please contact the sales department -at http://www.sencha.com/contact. - -Version: 6.0.1.250 Build date: 2015-09-02 17:27:43 (22ef9ff0ebf584ff525541be37e753a703cc044b) - -*/ -var Ext=Ext||{};if(!Ext.Toolbar){Ext.Toolbar={}}if(!Ext.app){Ext.app={}}if(!Ext.app.bind){Ext.app.bind={}}if(!Ext.app.domain){Ext.app.domain={}}if(!Ext.app.route){Ext.app.route={}}if(!Ext.button){Ext.button={}}if(!Ext.container){Ext.container={}}if(!Ext.core){Ext.core={}}if(!Ext.dashboard){Ext.dashboard={}}if(!Ext.data){Ext.data={}}if(!Ext.data.field){Ext.data.field={}}if(!Ext.data.flash){Ext.data.flash={}}if(!Ext.data.identifier){Ext.data.identifier={}}if(!Ext.data.matrix){Ext.data.matrix={}}if(!Ext.data.operation){Ext.data.operation={}}if(!Ext.data.proxy){Ext.data.proxy={}}if(!Ext.data.reader){Ext.data.reader={}}if(!Ext.data.request){Ext.data.request={}}if(!Ext.data.schema){Ext.data.schema={}}if(!Ext.data.session){Ext.data.session={}}if(!Ext.data.validator){Ext.data.validator={}}if(!Ext.data.writer){Ext.data.writer={}}if(!Ext.dd){Ext.dd={}}if(!Ext.direct){Ext.direct={}}if(!Ext.dom){Ext.dom={}}if(!Ext.dom.Element){Ext.dom.Element={}}if(!Ext.event){Ext.event={}}if(!Ext.event.gesture){Ext.event.gesture={}}if(!Ext.event.publisher){Ext.event.publisher={}}if(!Ext.flash){Ext.flash={}}if(!Ext.form){Ext.form={}}if(!Ext.form.Action){Ext.form.Action={}}if(!Ext.form.action){Ext.form.action={}}if(!Ext.form.field){Ext.form.field={}}if(!Ext.form.trigger){Ext.form.trigger={}}if(!Ext.fx){Ext.fx={}}if(!Ext.fx.animation){Ext.fx.animation={}}if(!Ext.fx.easing){Ext.fx.easing={}}if(!Ext.fx.layout){Ext.fx.layout={}}if(!Ext.fx.layout.card){Ext.fx.layout.card={}}if(!Ext.fx.runner){Ext.fx.runner={}}if(!Ext.fx.target){Ext.fx.target={}}if(!Ext.grid){Ext.grid={}}if(!Ext.grid.column){Ext.grid.column={}}if(!Ext.grid.feature){Ext.grid.feature={}}if(!Ext.grid.filters){Ext.grid.filters={}}if(!Ext.grid.filters.filter){Ext.grid.filters.filter={}}if(!Ext.grid.header){Ext.grid.header={}}if(!Ext.grid.locking){Ext.grid.locking={}}if(!Ext.grid.plugin){Ext.grid.plugin={}}if(!Ext.grid.property){Ext.grid.property={}}if(!Ext.grid.selection){Ext.grid.selection={}}if(!Ext.layout){Ext.layout={}}if(!Ext.layout.boxOverflow){Ext.layout.boxOverflow={}}if(!Ext.layout.component){Ext.layout.component={}}if(!Ext.layout.component.field){Ext.layout.component.field={}}if(!Ext.layout.container){Ext.layout.container={}}if(!Ext.layout.container.border){Ext.layout.container.border={}}if(!Ext.layout.container.boxOverflow){Ext.layout.container.boxOverflow={}}if(!Ext.list){Ext.list={}}if(!Ext.menu){Ext.menu={}}if(!Ext.mixin){Ext.mixin={}}if(!Ext.overrides){Ext.overrides={}}if(!Ext.overrides.app){Ext.overrides.app={}}if(!Ext.overrides.app.domain){Ext.overrides.app.domain={}}if(!Ext.overrides.dom){Ext.overrides.dom={}}if(!Ext.overrides.event){Ext.overrides.event={}}if(!Ext.overrides.event.publisher){Ext.overrides.event.publisher={}}if(!Ext.overrides.list){Ext.overrides.list={}}if(!Ext.overrides.plugin){Ext.overrides.plugin={}}if(!Ext.overrides.util){Ext.overrides.util={}}if(!Ext.panel){Ext.panel={}}if(!Ext.perf){Ext.perf={}}if(!Ext.picker){Ext.picker={}}if(!Ext.plugin){Ext.plugin={}}if(!Ext.promise){Ext.promise={}}if(!Ext.resizer){Ext.resizer={}}if(!Ext.scroll){Ext.scroll={}}if(!Ext.selection){Ext.selection={}}if(!Ext.slider){Ext.slider={}}if(!Ext.sparkline){Ext.sparkline={}}if(!Ext.state){Ext.state={}}if(!Ext.tab){Ext.tab={}}if(!Ext.tip){Ext.tip={}}if(!Ext.toolbar){Ext.toolbar={}}if(!Ext.tree){Ext.tree={}}if(!Ext.tree.plugin){Ext.tree.plugin={}}if(!Ext.util){Ext.util={}}if(!Ext.util.paintmonitor){Ext.util.paintmonitor={}}if(!Ext.util.sizemonitor){Ext.util.sizemonitor={}}if(!Ext.util.translatable){Ext.util.translatable={}}if(!Ext.ux){Ext.ux={}}if(!Ext.ux.form){Ext.ux.form={}}if(!Ext.ux.layout){Ext.ux.layout={}}if(!Ext.view){Ext.view={}}if(!Ext.window){Ext.window={}}(function(e){var b,m=["constructor","toString","valueOf","toLocaleString"],g={},q={},d=0,n,k,s,i,a,h,o,c,j,r=function(){var v,u;k=Ext.Base;s=Ext.ClassManager;for(v=m.length;v-->0;){u=(1<0;){v=b[w];u[v]=k[v]}return u},l=function(z,C,X,u,B,L,A,U,x,N,G){var v=function F(){return this.constructor.apply(this,arguments)||null},W=v,w={enumerableMembers:u&d,onCreated:G,onBeforeCreated:t,aliases:U},J=X.alternateClassName||[],S=Ext.global,O,R,T,I,Q,aa,Z,y,P,E,V,M,H,Y,K=s.alternateToName||s.maps.alternateToName,D=s.nameToAlternates||s.maps.nameToAlternates;for(T=b.length;T-->0;){Z=b[T];v[Z]=k[Z]}if(X.$isFunction){X=X(v)}w.data=X;E=X.statics;delete X.statics;X.$className=z;if("$className" in X){v.$className=X.$className}v.extend(C);P=v.prototype;v.xtype=X.xtype=B[0];if(B){P.xtypes=B}P.xtypesChain=L;P.xtypesMap=A;X.alias=U;W.triggerExtended(v,X,w);if(X.onClassExtended){v.onExtended(X.onClassExtended,v);delete X.onClassExtended}if(X.privates&&c){c.call(Ext.Class,v,X)}if(E){if(j){v.addStatics(E)}else{for(V in E){if(E.hasOwnProperty(V)){Y=E[V];if(Y&&Y.$isFunction&&!Y.$isClass&&Y!==Ext.emptyFn&&Y!==Ext.identityFn){v[V]=H=Y;H.$owner=v;H.$name=V}v[V]=Y}}}}if(X.inheritableStatics){v.addInheritableStatics(X.inheritableStatics);delete X.inheritableStatics}if(P.onClassExtended){W.onExtended(P.onClassExtended,W);delete P.onClassExtended}if(X.config){i.call(Ext.Class,v,X)}if(X.cachedConfig&&a){a.call(Ext.Class,v,X);delete X.cachedConfig}if(X.platformConfig&&h){h.call(Ext.Class,v,X);delete X.platformConfig}if(X.deprecated&&o){o.call(Ext.ClassManager,z,v,X)}w.onBeforeCreated(v,w.data,w);for(T=0,Q=x&&x.length;T1?parseInt(F[2]):0;if(K){D=true}}else{K=0}E[N]=K}if(E.ie){var G=document.documentMode;if(G>=8){E.ie=G}}K=E.ie||false;L=Math.max(K,w.maxIEVersion);for(I=8;I<=L;++I){H="ie"+I;E[H+"m"]=K?K<=I:0;E[H]=K?K===I:0;E[H+"p"]=K?K>=I:0}return E},y=function(){var E={},J,K,M,G,H,F,D,I,L;M=c(w.osPrefixes);H=M.length;for(G=0,L=0;G1?parseFloat(F[F.length-1]):0}if(I){L++}E[K]=I}M=c(w.fallbackOSPrefixes);H=M.length;for(G=0;G1){z=u[1];if(z==="false"||z==="0"){z=false}}y[s]=z}}return y},filterPlatform:function(t,x){t=e.concat(t||e);x=e.concat(x||e);var w=t.length,v=x.length,s=(!w&&v),u,r;for(u=0;u0&&((v=t.charAt(w-1))==="?"||v==="&")){r=t.indexOf("&",w);r=(r<0)?"":t.substring(r);if(r&&v==="?"){++w;r=r.substring(1)}t=t.substring(0,w-1)+r}return t},getConfig:function(r){return r?d.config[r]:d.config},setConfig:function(r,u){if(typeof r==="string"){d.config[r]=u}else{for(var t in r){d.setConfig(t,r[t])}}return d},getHead:function(){return d.docHead||(d.docHead=o.head||o.getElementsByTagName("head")[0])},create:function(t,u,r){var s=r||{};s.url=t;s.key=u;return d.scripts[u]=new j(s)},getEntry:function(s,r){var t=d.canonicalUrl(s),u=d.scripts[t];if(!u){u=d.create(s,t,r)}return u},registerContent:function(s,t,u){var r={content:u,loaded:true,css:t==="css"};return d.getEntry(s,r)},processRequest:function(s,r){s.loadEntries(r)},load:function(r){var r=new b(r);if(r.sync||d.syncMode){return d.loadSync(r)}if(d.currentRequest){r.getEntries();d.suspendedQueue.push(r)}else{d.currentRequest=r;d.processRequest(r,false)}return d},loadSync:function(r){var r=new b(r);d.syncMode++;d.processRequest(r,true);d.syncMode--;return d},loadBasePrefix:function(r){r=new b(r);r.prependBaseUrl=true;return d.load(r)},loadSyncBasePrefix:function(r){r=new b(r);r.prependBaseUrl=true;return d.loadSync(r)},requestComplete:function(s){var r;if(d.currentRequest===s){d.currentRequest=null;while(d.suspendedQueue.length>0){r=d.suspendedQueue.shift();if(!r.done){d.load(r);break}}}if(!d.currentRequest&&d.suspendedQueue.length==0){d.fireListeners()}},isLoading:function(){return !d.currentRequest&&d.suspendedQueue.length==0},fireListeners:function(){var r;while(d.isLoading()&&(r=d.listeners.shift())){r()}},onBootReady:function(r){if(!d.isLoading()){r()}else{d.listeners.push(r)}},getPathsFromIndexes:function(s,r){return b.prototype.getPathsFromIndexes(s,r)},createLoadOrderMap:function(r){return b.prototype.createLoadOrderMap(r)},fetch:function(r,s,A,u){u=(u===undefined)?!!s:u;var z=new XMLHttpRequest(),B,w,x,t=false,y=function(){if(z&&z.readyState==4){w=(z.status===1223)?204:(z.status===0&&((self.location||{}).protocol==="file:"||(self.location||{}).protocol==="ionp:"))?200:z.status;x=z.responseText;B={content:x,status:w,exception:t};if(s){s.call(A,B)}z=null}};if(u){z.onreadystatechange=y}try{z.open("GET",r,u);z.send(null)}catch(v){t=v;y();return B}if(!u){y()}return B},notifyAll:function(r){r.notifyRequests()}};function b(r){if(r.$isRequest){return r}var r=r.url?r:{url:r},s=r.url,t=s.charAt?[s]:s,u=r.charset||d.config.charset;a(r,{urls:t,charset:u});a(this,r)}b.prototype={$isRequest:true,createLoadOrderMap:function(s){var r=s.length,t={},v,u;for(v=0;v0){setTimeout(function(){u.call(t,v)},r)}else{u.call(t,v)}}v.fireListeners();d.requestComplete(v)}},onDone:function(t){var s=this,r=s.listeners||(s.listeners=[]);if(s.done){t(s)}else{r.push(t)}},fireListeners:function(){var r=this.listeners,s;if(r){while((s=r.shift())){s(this)}}}};function j(s){if(s.$isEntry){return s}var x=s.charset||d.config.charset,w=Ext.manifest,r=w&&w.loader,t=(s.cache!==undefined)?s.cache:(r&&r.cache),v,u;if(d.config.disableCaching){if(t===undefined){t=!d.config.disableCaching}if(t===false){v=+new Date()}else{if(t!==true){v=t}}if(v){u=(r&&r.cacheParam)||d.config.disableCachingParam;v=u+"="+v}}a(s,{charset:x,buster:v,requests:[]});a(this,s)}j.prototype={$isEntry:true,done:false,evaluated:false,loaded:false,isCrossDomain:function(){var r=this;if(r.crossDomain===undefined){r.crossDomain=(r.getLoadUrl().indexOf(d.origin)!==0)}return r.crossDomain},isCss:function(){var s=this;if(s.css===undefined){if(s.url){var r=d.assetConfig[s.url];s.css=r?r.type==="css":g.test(s.url)}else{s.css=false}}return this.css},getElement:function(r){var t=this,s=t.el;if(!s){if(t.isCss()){r=r||"link";s=o.createElement(r);if(r=="link"){s.rel="stylesheet";t.prop="href"}else{t.prop="textContent"}s.type="text/css"}else{r=r||"script";s=o.createElement(r);s.type="text/javascript";t.prop="src";if(t.charset){s.charset=t.charset}if(d.hasAsync){s.async=false}}t.el=s}return s},getLoadUrl:function(){var s=this,r=d.canonicalUrl(s.url);if(!s.loadUrl){s.loadUrl=!!s.buster?(r+(r.indexOf("?")===-1?"?":"&")+s.buster):r}return s.loadUrl},fetch:function(u){var s=this.getLoadUrl(),t=!!u.async,r=u.complete;d.fetch(s,r,this,t)},onContentLoaded:function(s){var w=this,r=s.status,v=s.content,u=s.exception,t=this.getLoadUrl();w.loaded=true;if((u||r===0)&&!i.phantom){w.error=true;w.evaluated=true}else{if((r>=200&&r<300)||r===304||i.phantom||(r===0&&v.length>0)){w.content=v}else{w.error=true;w.evaluated=true}}},createLoadElement:function(v){var t=this,s=t.getElement(),r=function(){if(this.readyState==="loaded"||this.readyState==="complete"){if(v){v()}}},u=function(){t.error=true;if(v){v()}};t.preserve=true;s.onerror=u;if(d.hasReadyState){s.onreadystatechange=r}else{s.onload=v}s[t.prop]=t.getLoadUrl()},onLoadElementReady:function(){d.getHead().appendChild(this.getElement());this.evaluated=true},inject:function(w,v){var x=this,y=d.getHead(),r=x.url,z=x.key,s,t,u,A;if(x.isCss()){x.preserve=true;A=z.substring(0,z.lastIndexOf("/")+1);s=o.createElement("base");s.href=A;if(y.firstChild){y.insertBefore(s,y.firstChild)}else{y.appendChild(s)}s.href=s.href;if(r){w+="\n/*# sourceURL="+z+" */"}t=x.getElement("style");u=("styleSheet" in t);y.appendChild(s);if(u){y.appendChild(t);t.styleSheet.cssText=w}else{t.textContent=w;y.appendChild(t)}y.removeChild(s)}else{if(r){w+="\n//# sourceURL="+z}Ext.globalEval(w)}return x},loadCrossDomain:function(){var s=this,r=function(){s.loaded=s.evaluated=s.done=true;s.notifyRequests()};s.createLoadElement(function(){r()});s.evaluateLoadElement();return false},loadElement:function(){var s=this,r=function(){s.loaded=s.evaluated=s.done=true;s.notifyRequests()};s.createLoadElement(function(){r()});s.evaluateLoadElement();return true},loadSync:function(){var r=this;r.fetch({async:false,complete:function(s){r.onContentLoaded(s)}});r.evaluate();r.notifyRequests()},load:function(s){var r=this;if(!r.loaded){if(r.loading){return false}r.loading=true;if(!s){if(r.isCrossDomain()){return r.loadCrossDomain()}else{if(!r.isCss()&&d.hasReadyState){r.createLoadElement(function(){r.loaded=true;r.notifyRequests()})}else{if(d.useElements&&!(r.isCss()&&i.phantom)){return r.loadElement()}else{r.fetch({async:!s,complete:function(t){r.onContentLoaded(t);r.notifyRequests()}})}}}}else{r.loadSync()}}return true},evaluateContent:function(){this.inject(this.content);this.content=null},evaluateLoadElement:function(){d.getHead().appendChild(this.getElement())},evaluate:function(){var r=this;if(!r.evaluated){if(r.evaluating){return}r.evaluating=true;if(r.content!==undefined){r.evaluateContent()}else{if(!r.error){r.evaluateLoadElement()}}r.evaluated=r.done=true;r.cleanup()}},cleanup:function(){var t=this,s=t.el,u;if(!s){return}if(!t.preserve){t.el=null;s.parentNode.removeChild(s);for(u in s){try{if(u!==t.prop){s[u]=null}delete s[u]}catch(r){}}}s.onload=s.onerror=s.onreadystatechange=h},notifyRequests:function(){var u=this.requests,r=u.length,s,t;for(s=0;s0){while((s=r.shift())){s(this)}}}};Ext.disableCacheBuster=function(s,t){var r=new Date();r.setTime(r.getTime()+(s?10*365:-1)*24*60*60*1000);r=r.toGMTString();o.cookie="ext-cache=1; expires="+r+"; path="+(t||"/")};d.init();return d}(function(){}));Ext.globalEval=Ext.globalEval||(this.execScript?function(a){execScript(a)}:function($$code){eval.call(window,$$code)});if(!Function.prototype.bind){(function(){var a=Array.prototype.slice,b=function(d){var c=a.call(arguments,1),e=this;if(c.length){return function(){var g=arguments;return e.apply(d,g.length?c.concat(a.call(g)):c)}}c=null;return function(){return e.apply(d,arguments)}};Function.prototype.bind=b;b.$extjs=true}())}Ext.setResourcePath=function(c,b){var a=Ext.manifest||(Ext.manifest={}),d=a.resources||(a.resources={});if(a){if(typeof c!=="string"){Ext.apply(d,c)}else{d[c]=b}a.resources=d}};Ext.getResourcePath=function(g,e,a){if(typeof g!=="string"){e=g.pool;a=g.packageName;g=g.path}var d=Ext.manifest,h=d&&d.resources,c=h[e],b=[];if(c==null){c=h.path;if(c==null){c="resources"}}if(c){b.push(c)}if(a){b.push(a)}b.push(g);return b.join("/")};var Ext=Ext||{};(function(){var b=this,h=Object.prototype,c=h.toString,o=["valueOf","toLocaleString","toString","constructor"],l=function(){},g=function(){},j=function(i){return i},n=function(){var i=n.caller.caller;return i.$owner.prototype[i.$name].apply(this,arguments)},a=Ext.manifest||{},k,d=/\[object\s*(?:Array|Arguments|\w*Collection|\w*List|HTML\s+document\.all\s+class)\]/,e=/^\\?\/Date\(([-+])?(\d+)(?:[+-]\d{4})?\)\\?\/$/;Ext.global=b;Ext.now=Date.now||(Date.now=function(){return +new Date()});Ext.ticks=(b.performance&&b.performance.now)?function(){return performance.now()}:Ext.now;Ext._startTime=Ext.ticks();l.$nullFn=j.$nullFn=l.$emptyFn=j.$identityFn=g.$nullFn=true;g.$privacy="framework";Ext.suspendLayouts=Ext.resumeLayouts=l;for(k in {toString:1}){o=null}Ext.enumerables=o;Ext.apply=function(s,r,u){if(u){Ext.apply(s,u)}if(s&&r&&typeof r==="object"){var t,q,p;for(t in r){s[t]=r[t]}if(o){for(q=o.length;q--;){p=o[q];if(r.hasOwnProperty(p)){s[p]=r[p]}}}}return s};function m(r,i,s){var p,q;for(p in s){if(s.hasOwnProperty(p)){q=s[p];if(typeof q==="function"){q.$name=p;q.$owner=i;q.$previous=r.hasOwnProperty(p)?r[p]:n}r[p]=q}}}Ext.buildSettings=Ext.apply({baseCSSPrefix:"x-"},Ext.buildSettings||{});Ext.apply(Ext,{idSeed:0,idPrefix:"ext-",isSecure:/^https/i.test(window.location.protocol),enableGarbageCollector:false,enableListenerCollection:true,name:Ext.sandboxName||"Ext",privateFn:g,emptyFn:l,identityFn:j,frameStartTime:Ext.now(),manifest:a,enableAria:true,enableAriaButtons:true,enableAriaPanels:true,startsWithHashRe:/^#/,validIdRe:/^[a-z_][a-z0-9\-_]*$/i,BLANK_IMAGE_URL:"data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==",makeIdSelector:function(i){return"#"+i},id:function(p,i){if(p&&p.id){return p.id}var q=(i||Ext.idPrefix)+(++Ext.idSeed);if(p){p.id=q}return q},returnId:function(i){return i.getId()},returnTrue:function(){return true},emptyString:new String(),baseCSSPrefix:Ext.buildSettings.baseCSSPrefix,$eventNameMap:{},$vendorEventRe:/^(Moz.+|MS.+|webkit.+)/,canonicalEventName:function(i){return Ext.$eventNameMap[i]||(Ext.$eventNameMap[i]=(Ext.$vendorEventRe.test(i)?i:i.toLowerCase()))},applyIf:function(p,i){var q;if(p){for(q in i){if(p[q]===undefined){p[q]=i[q]}}}return p},destroy:function(){var r=arguments.length,q,p;for(q=0;q0){s--;q[s]="var Ext=window."+Ext.name+";"+q[s]}}i=q.join("");r=p[i];if(!r){r=Function.prototype.constructor.apply(Function.prototype,q);p[i]=r}return r},functionFactory:function(){var i=Array.prototype.slice.call(arguments),p;if(Ext.isSandboxed){p=i.length;if(p>0){p--;i[p]="var Ext=window."+Ext.name+";"+i[p]}}return Function.prototype.constructor.apply(Function.prototype,i)},Logger:{log:function(p,i){if(p&&b.console){if(!i||!(i in b.console)){i="log"}p="["+i.toUpperCase()+"] "+p;b.console[i](p)}},verbose:function(i){this.log(i,"verbose")},info:function(i){this.log(i,"info")},warn:function(i){this.log(i,"warn")},error:function(i){throw new Error(i)},deprecate:function(i){this.log(i,"warn")}}||{verbose:l,log:l,info:l,warn:l,error:function(i){throw new Error(i)},deprecate:l},getElementById:function(i){return document.getElementById(i)},splitAndUnescape:(function(){var i={};return function(r,q){if(!r){return[]}else{if(!q){return[r]}}var t=i[q]||(i[q]=new RegExp("\\\\"+q,"g")),p=[],u,s;u=r.split(q);while((s=u.shift())!==undefined){while(s.charAt(s.length-1)==="\\"&&u.length>0){s=s+q+u.shift()}s=s.replace(t,q);p.push(s)}return p}})()});Ext.returnTrue.$nullFn=Ext.returnId.$nullFn=true}());Ext.platformTags.modern=!(Ext.platformTags.classic=Ext.isClassic=true);(function(){function a(){var c=this,b=c.sourceClass,e=c.sourceMethod,d=c.msg;if(e){if(d){e+="(): ";e+=d}else{e+="()"}}if(b){e=e?(b+"."+e):b}return e||d||""}Ext.Error=function(c){if(Ext.isString(c)){c={msg:c}}var b=new Error();Ext.apply(b,c);b.message=b.message||b.msg;b.toString=a;return b};Ext.apply(Ext.Error,{ignore:false,raise:function(d){d=d||{};if(Ext.isString(d)){d={msg:d}}var c=this,g=c.raise.caller,e,b;if(g===Ext.raise){g=g.caller}if(g){if(!d.sourceMethod&&(b=g.$name)){d.sourceMethod=b}if(!d.sourceClass&&(b=g.$owner)&&(b=b.$className)){d.sourceClass=b}}if(c.handle(d)!==true){e=a.call(d);throw new Ext.Error(d)}},handle:function(){return this.ignore}})})();Ext.deprecated=function(a){return Ext.emptyFn};Ext.raise=function(){Ext.Error.raise.apply(Ext.Error,arguments)};Ext.Array=(function(){var c=Array.prototype,l=c.slice,n=(function(){var v=[],e,u=20;if(!v.splice){return false}while(u--){v.push("A")}v.splice(15,0,"F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F");e=v.length;v.splice(13,0,"XXX");if(e+1!==v.length){return false}return true}()),m="indexOf" in c,h=true;function k(x,u){var e=x.length,w=new Array(e),v;for(v=0;vD){for(y=e;y--;){B[v+y]=B[D+y]}}}if(F&&C===x){B.length=x;B.push.apply(B,E)}else{B.length=x+F;for(y=0;y>1;y=z(x,A[u]);if(y>=0){v=u+1}else{if(y<0){e=u-1}}}return v},defaultCompare:function(e,u){return(eu)?1:0)},lexicalCompare:function(e,u){e=String(e);u=String(u);return(eu)?1:0)},each:function(y,w,v,e){y=a.from(y);var u,x=y.length;if(e!==true){for(u=0;u-1;u--){if(w.call(v||y[u],y[u],u,y)===false){return u}}}return true},forEach:("forEach" in c)?function(v,u,e){return v.forEach(u,e)}:function(x,v,u){for(var e=0,w=x.length;e=0&&u>>0,u=e;if(arguments.length<3){while(true){if(v in y){u=y[v++];break}if(++v>=w){throw new TypeError("Reduce of empty array with no initial value")}}}for(;ve){e=v}}}return e},mean:function(e){return e.length>0?a.sum(e)/e.length:undefined},sum:function(x){var u=0,e,w,v;for(e=0,w=x.length;eu?1:-1,e;for(e=u;e!=w;e+=x){y[e]=y[e+x]}y[w]=v},replace:q,splice:r,push:function(w){var e=arguments.length,v=1,u;if(w===undefined){w=[]}else{if(!Ext.isArray(w)){w=[w]}}for(;v=p){q=0}else{q=p-q}}if(q===0){r=t+r}else{if(q>=r.length){r+=t}else{r=r.substr(0,q)+t+r.substr(q)}}return r},startsWith:function(r,t,q){var p=c(r,t);if(p){if(q){r=r.toLowerCase();t=t.toLowerCase()}p=r.lastIndexOf(t,0)===0}return p},endsWith:function(t,q,r){var p=c(t,q);if(p){if(r){t=t.toLowerCase();q=q.toLowerCase()}p=t.indexOf(q,t.length-q.length)!==-1}return p},createVarName:function(p){return p.replace(l,"")},htmlEncode:function(p){return(!p)?p:String(p).replace(h,g)},htmlDecode:function(p){return(!p)?p:String(p).replace(d,k)},hasHtmlCharacters:function(p){return h.test(p)},addCharacterEntities:function(q){var p=[],t=[],r,s;for(r in q){s=q[r];a[r]=s;e[s]=r;p.push(s);t.push(r)}h=new RegExp("("+p.join("|")+")","g");d=new RegExp("("+t.join("|")+"|&#[0-9]{1,5};)","g")},resetCharacterEntities:function(){e={};a={};this.addCharacterEntities({"&":"&",">":">","<":"<",""":'"',"'":"'"})},urlAppend:function(q,p){if(!Ext.isEmpty(p)){return q+(q.indexOf("?")===-1?"?":"&")+p}return q},trim:function(p){if(p){p=p.replace(i,"")}return p||""},capitalize:function(p){if(p){p=p.charAt(0).toUpperCase()+p.substr(1)}return p||""},uncapitalize:function(p){if(p){p=p.charAt(0).toLowerCase()+p.substr(1)}return p||""},ellipsis:function(r,q,s){if(r&&r.length>q){if(s){var t=r.substr(0,q-2),p=Math.max(t.lastIndexOf(" "),t.lastIndexOf("."),t.lastIndexOf("!"),t.lastIndexOf("?"));if(p!==-1&&p>=(q-15)){return t.substr(0,p)+"..."}}return r.substr(0,q-3)+"..."}return r},escapeRegex:function(p){return p.replace(b,"\\$1")},createRegex:function(t,s,q,p){var r=t;if(t!=null&&!t.exec){r=n.escapeRegex(String(t));if(s!==false){r="^"+r}if(q!==false){r+="$"}r=new RegExp(r,(p!==false)?"i":"")}return r},escape:function(p){return p.replace(m,"\\$1")},toggle:function(q,r,p){return q===r?p:r},leftPad:function(q,r,s){var p=String(q);s=s||" ";while(p.length daysInMonth) {","d = daysInMonth;","}","}","h = from(h, from(def.h, dt.getHours()));","i = from(i, from(def.i, dt.getMinutes()));","s = from(s, from(def.s, dt.getSeconds()));","ms = from(ms, from(def.ms, dt.getMilliseconds()));","if(z >= 0 && y >= 0){","v = me.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), me.YEAR, y < 100 ? y - 100 : 0);","v = !strict? v : (strict === true && (z <= 364 || (me.isLeapYear(v) && z <= 365))? me.add(v, me.DAY, z) : null);","}else if(strict === true && !me.isValid(y, m + 1, d, h, i, s, ms)){","v = null;","}else{","if (W) {","year = y || (new Date()).getFullYear();","jan4 = new Date(year, 0, 4, 0, 0, 0);","d = jan4.getDay();","week1monday = new Date(jan4.getTime() - ((d === 0 ? 6 : d - 1) * 86400000));","v = Ext.Date.clearTime(new Date(week1monday.getTime() + ((W - 1) * 604800000 + 43200000)));","} else {","v = me.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), me.YEAR, y < 100 ? y - 100 : 0);","}","}","}","}","if(v){","if(zz != null){","v = me.add(v, me.SECOND, -v.getTimezoneOffset() * 60 - zz);","}else if(o){","v = me.add(v, me.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));","}","}","return (v != null) ? v : null;"].join("\n");if(!Date.prototype.toISOString){Date.prototype.toISOString=function(){var m=this;return d(m.getUTCFullYear(),4,"0")+"-"+d(m.getUTCMonth()+1,2,"0")+"-"+d(m.getUTCDate(),2,"0")+"T"+d(m.getUTCHours(),2,"0")+":"+d(m.getUTCMinutes(),2,"0")+":"+d(m.getUTCSeconds(),2,"0")+"."+d(m.getUTCMilliseconds(),3,"0")+"Z"}}function j(n){var m=Array.prototype.slice.call(arguments,1);return n.replace(c,function(o,p){return m[p]})}return g={now:e.now,toString:function(m){if(!m){m=new e()}return m.getFullYear()+"-"+d(m.getMonth()+1,2,"0")+"-"+d(m.getDate(),2,"0")+"T"+d(m.getHours(),2,"0")+":"+d(m.getMinutes(),2,"0")+":"+d(m.getSeconds(),2,"0")},getElapsed:function(n,m){return Math.abs(n-(m||g.now()))},useStrict:false,formatCodeToRegex:function(n,m){var o=g.parseCodes[n];if(o){o=typeof o==="function"?o():o;g.parseCodes[n]=o}return o?Ext.applyIf({c:o.c?j(o.c,m||"{0}"):o.c},o):{g:0,c:null,s:Ext.String.escapeRegex(n)}},parseFunctions:{MS:function(n,m){var o=(n||"").match(i);return o?new e(((o[1]||"")+o[2])*1):null},time:function(n,m){var o=parseInt(n,10);if(o||o===0){return new e(o)}return null},timestamp:function(n,m){var o=parseInt(n,10);if(o||o===0){return new e(o*1000)}return null}},parseRegexes:[],formatFunctions:{MS:function(){return"\\/Date("+this.getTime()+")\\/"},time:function(){return this.getTime().toString()},timestamp:function(){return g.format(this,"U")}},y2kYear:50,MILLI:"ms",SECOND:"s",MINUTE:"mi",HOUR:"h",DAY:"d",MONTH:"mo",YEAR:"y",defaults:{},dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNumbers:{January:0,Jan:0,February:1,Feb:1,March:2,Mar:2,April:3,Apr:3,May:4,June:5,Jun:5,July:6,Jul:6,August:7,Aug:7,September:8,Sep:8,October:9,Oct:9,November:10,Nov:10,December:11,Dec:11},defaultFormat:"m/d/Y",getShortMonthName:function(m){return g.monthNames[m].substring(0,3)},getShortDayName:function(m){return g.dayNames[m].substring(0,3)},getMonthNumber:function(m){return g.monthNumbers[m.substring(0,1).toUpperCase()+m.substring(1,3).toLowerCase()]},formatContainsHourInfo:function(m){return a.test(m.replace(l,""))},formatContainsDateInfo:function(m){return h.test(m.replace(l,""))},unescapeFormat:function(m){return m.replace(k,"")},formatCodes:{d:"Ext.String.leftPad(m.getDate(), 2, '0')",D:"Ext.Date.getShortDayName(m.getDay())",j:"m.getDate()",l:"Ext.Date.dayNames[m.getDay()]",N:"(m.getDay() ? m.getDay() : 7)",S:"Ext.Date.getSuffix(m)",w:"m.getDay()",z:"Ext.Date.getDayOfYear(m)",W:"Ext.String.leftPad(Ext.Date.getWeekOfYear(m), 2, '0')",F:"Ext.Date.monthNames[m.getMonth()]",m:"Ext.String.leftPad(m.getMonth() + 1, 2, '0')",M:"Ext.Date.getShortMonthName(m.getMonth())",n:"(m.getMonth() + 1)",t:"Ext.Date.getDaysInMonth(m)",L:"(Ext.Date.isLeapYear(m) ? 1 : 0)",o:"(m.getFullYear() + (Ext.Date.getWeekOfYear(m) == 1 && m.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(m) >= 52 && m.getMonth() < 11 ? -1 : 0)))",Y:"Ext.String.leftPad(m.getFullYear(), 4, '0')",y:"('' + m.getFullYear()).substring(2, 4)",a:"(m.getHours() < 12 ? 'am' : 'pm')",A:"(m.getHours() < 12 ? 'AM' : 'PM')",g:"((m.getHours() % 12) ? m.getHours() % 12 : 12)",G:"m.getHours()",h:"Ext.String.leftPad((m.getHours() % 12) ? m.getHours() % 12 : 12, 2, '0')",H:"Ext.String.leftPad(m.getHours(), 2, '0')",i:"Ext.String.leftPad(m.getMinutes(), 2, '0')",s:"Ext.String.leftPad(m.getSeconds(), 2, '0')",u:"Ext.String.leftPad(m.getMilliseconds(), 3, '0')",O:"Ext.Date.getGMTOffset(m)",P:"Ext.Date.getGMTOffset(m, true)",T:"Ext.Date.getTimezone(m)",Z:"(m.getTimezoneOffset() * -60)",c:function(){var q="Y-m-dTH:i:sP",o=[],n,m=q.length,p;for(n=0;n me.y2kYear ? 1900 + ty : 2000 + ty;\n",s:"(\\d{2})"},a:{g:1,c:"if (/(am)/i.test(results[{0}])) {\nif (!h || h == 12) { h = 0; }\n} else { if (!h || h < 12) { h = (h || 0) + 12; }}",s:"(am|pm|AM|PM)",calcAtEnd:true},A:{g:1,c:"if (/(am)/i.test(results[{0}])) {\nif (!h || h == 12) { h = 0; }\n} else { if (!h || h < 12) { h = (h || 0) + 12; }}",s:"(AM|PM|am|pm)",calcAtEnd:true},g:{g:1,c:"h = parseInt(results[{0}], 10);\n",s:"(1[0-2]|[0-9])"},G:{g:1,c:"h = parseInt(results[{0}], 10);\n",s:"(2[0-3]|1[0-9]|[0-9])"},h:{g:1,c:"h = parseInt(results[{0}], 10);\n",s:"(1[0-2]|0[1-9])"},H:{g:1,c:"h = parseInt(results[{0}], 10);\n",s:"(2[0-3]|[0-1][0-9])"},i:{g:1,c:"i = parseInt(results[{0}], 10);\n",s:"([0-5][0-9])"},s:{g:1,c:"s = parseInt(results[{0}], 10);\n",s:"([0-5][0-9])"},u:{g:1,c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",s:"(\\d+)"},O:{g:1,c:["o = results[{0}];","var sn = o.substring(0,1),","hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),","mn = o.substring(3,5) % 60;","o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n"].join("\n"),s:"([+-]\\d{4})"},P:{g:1,c:["o = results[{0}];","var sn = o.substring(0,1),","hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),","mn = o.substring(4,6) % 60;","o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n"].join("\n"),s:"([+-]\\d{2}:\\d{2})"},T:{g:0,c:null,s:"[A-Z]{1,5}"},Z:{g:1,c:"zz = results[{0}] * 1;\nzz = (-43200 <= zz && zz <= 50400)? zz : null;\n",s:"([+-]?\\d{1,5})"},c:function(){var o=[],m=[g.formatCodeToRegex("Y",1),g.formatCodeToRegex("m",2),g.formatCodeToRegex("d",3),g.formatCodeToRegex("H",4),g.formatCodeToRegex("i",5),g.formatCodeToRegex("s",6),{c:"ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"},{c:["if(results[8]) {","if(results[8] == 'Z'){","zz = 0;","}else if (results[8].indexOf(':') > -1){",g.formatCodeToRegex("P",8).c,"}else{",g.formatCodeToRegex("O",8).c,"}","}"].join("\n")}],p,n;for(p=0,n=m.length;p0?"-":"+")+Ext.String.leftPad(Math.floor(Math.abs(o)/60),2,"0")+(n?":":"")+Ext.String.leftPad(Math.abs(o%60),2,"0")},getDayOfYear:function(p){var o=0,r=g.clone(p),n=p.getMonth(),q;for(q=0,r.setDate(1),r.setMonth(0);q28){m=Math.min(m,g.getLastDateOfMonth(g.add(g.getFirstDateOfMonth(o),g.MONTH,r)).getDate())}s.setDate(m);s.setMonth(o.getMonth()+r);break;case g.YEAR:m=o.getDate();if(m>28){m=Math.min(m,g.getLastDateOfMonth(g.add(g.getFirstDateOfMonth(o),g.YEAR,r)).getDate())}s.setDate(m);s.setFullYear(o.getFullYear()+r);break}}if(q){switch(n.toLowerCase()){case g.MILLI:p=1;break;case g.SECOND:p=1000;break;case g.MINUTE:p=1000*60;break;case g.HOUR:p=1000*60*60;break;case g.DAY:p=1000*60*60*24;break;case g.MONTH:m=g.getDaysInMonth(s);p=1000*60*60*24*m;break;case g.YEAR:m=(g.isLeapYear(s)?366:365);p=1000*60*60*24*m;break}if(p){s.setTime(s.getTime()+p*q)}}return s},subtract:function(n,m,o){return g.add(n,m,-o)},between:function(n,p,m){var o=n.getTime();return p.getTime()<=o&&o<=m.getTime()},compat:function(){var u,v=["useStrict","formatCodeToRegex","parseFunctions","parseRegexes","formatFunctions","y2kYear","MILLI","SECOND","MINUTE","HOUR","DAY","MONTH","YEAR","defaults","dayNames","monthNames","monthNumbers","getShortMonthName","getShortDayName","getMonthNumber","formatCodes","isValid","parseDate","getFormatCode","createFormat","createParser","parseCodes"],t=["dateFormat","format","getTimezone","getGMTOffset","getDayOfYear","getWeekOfYear","isLeapYear","getFirstDayOfMonth","getLastDayOfMonth","getDaysInMonth","getSuffix","clone","isDST","clearTime","add","between"],n=v.length,m=t.length,q,r,o;for(o=0;om){return o-1}return o;case g.YEAR:o=m.getFullYear()-n.getFullYear();if(g.add(n,p,o)>m){return o-1}else{return o}}},align:function(n,p,o){var m=new e(+n);switch(p.toLowerCase()){case g.MILLI:return m;case g.SECOND:m.setUTCSeconds(m.getUTCSeconds()-m.getUTCSeconds()%o);m.setUTCMilliseconds(0);return m;case g.MINUTE:m.setUTCMinutes(m.getUTCMinutes()-m.getUTCMinutes()%o);m.setUTCSeconds(0);m.setUTCMilliseconds(0);return m;case g.HOUR:m.setUTCHours(m.getUTCHours()-m.getUTCHours()%o);m.setUTCMinutes(0);m.setUTCSeconds(0);m.setUTCMilliseconds(0);return m;case g.DAY:if(o===7||o===14){m.setUTCDate(m.getUTCDate()-m.getUTCDay()+1)}m.setUTCHours(0);m.setUTCMinutes(0);m.setUTCSeconds(0);m.setUTCMilliseconds(0);return m;case g.MONTH:m.setUTCMonth(m.getUTCMonth()-(m.getUTCMonth()-1)%o,1);m.setUTCHours(0);m.setUTCMinutes(0);m.setUTCSeconds(0);m.setUTCMilliseconds(0);return m;case g.YEAR:m.setUTCFullYear(m.getUTCFullYear()-m.getUTCFullYear()%o,1,1);m.setUTCHours(0);m.setUTCMinutes(0);m.setUTCSeconds(0);m.setUTCMilliseconds(0);return n}}}}());Ext.Function=(function(){var b=0,m,e=[],n=[],i=0,j={},h=window,d=Ext.global,g=!!(d.setImmediate&&d.clearImmediate),l=h.requestAnimationFrame||h.webkitRequestAnimationFrame||h.mozRequestAnimationFrame||h.oRequestAnimationFrame||function(r){var o=Ext.now(),p=Math.max(0,16-(o-b)),q=h.setTimeout(function(){r(o+p)},p);b=o+p;return q},c=function(){var o=e.length,r,p,q;m=null;for(p=0;p0){return setTimeout(function(){if(Ext.elevateFunction){Ext.elevateFunction(s)}else{s()}},q)}s();return 0},interval:function(s,q,r,p,o){s=Ext.Function.bind(s,r,p,o);return setInterval(function(){if(Ext.elevateFunction){Ext.elevateFunction(s)}else{s()}},q)},createSequence:function(p,q,o){if(!q){return p}else{return function(){var r=p.apply(this,arguments);q.apply(o||this,arguments);return r}}},createBuffered:function(s,p,r,q){var o;return function(){var u=q||Array.prototype.slice.call(arguments,0),t=r||this;if(o){clearTimeout(o)}o=setTimeout(function(){if(Ext.elevateFunction){Ext.elevateFunction(s,t,u)}else{s.apply(t,u)}},p)}},createAnimationFrame:function(r,q,p,s){var o;s=s||3;return function(){var t=p||Array.prototype.slice.call(arguments,0);q=q||this;if(s===3&&o){k.cancelAnimationFrame(o)}if((s&1)||!o){o=k.requestAnimationFrame(function(){o=null;r.apply(q,t)})}}},requestAnimationFrame:function(r,q,o){var s=++i,p=Array.prototype.slice.call(arguments,0);p[3]=s;j[s]=1;e.push(p);if(!m){m=l(Ext.elevateFunction?a:c)}return s},cancelAnimationFrame:function(o){delete j[o]},createThrottled:function(s,p,r){var t=0,o,q,v,u=function(){if(Ext.elevateFunction){Ext.elevateFunction(s,r,q)}else{s.apply(r,q)}t=Ext.now();v=null};return function(){if(!r){r=this}o=Ext.now()-t;q=arguments;if(o>=p){clearTimeout(v);u()}else{if(!v){v=Ext.defer(u,p-o)}}}},createBarrier:function(q,p,o){return function(){if(!--q){p.apply(o,arguments)}}},interceptBefore:function(p,o,r,q){var s=p[o]||Ext.emptyFn;return(p[o]=function(){var t=r.apply(q||this,arguments);s.apply(this,arguments);return t})},interceptAfter:function(p,o,r,q){var s=p[o]||Ext.emptyFn;return(p[o]=function(){s.apply(this,arguments);return r.apply(q||this,arguments)})},makeCallback:function(p,o){return function(){return o[p].apply(o,arguments)}},memoize:function(r,q,o){var p={},s=o&&Ext.isFunction(o);return function(u){var t=s?o.apply(q,arguments):u;if(!(t in p)){p[t]=r.apply(q,arguments)}return p[t]}}};Ext.asap=g?function(p,o,q){if(o!=null||q!=null){p=k.bind(p,o,q)}return setImmediate(function(){if(Ext.elevateFunction){Ext.elevateFunction(p)}else{p()}})}:function(p,o,q){if(o!=null||q!=null){p=k.bind(p,o,q)}return setTimeout(function(){if(Ext.elevateFunction){Ext.elevateFunction(p)}else{p()}},0,true)},Ext.asapCancel=g?function(o){clearImmediate(o)}:function(o){clearTimeout(o)};Ext.defer=k.defer;Ext.interval=k.interval;Ext.pass=k.pass;Ext.bind=k.bind;Ext.deferCallback=k.requestAnimationFrame;return k})();Ext.Number=(new function(){var d=this,c=(0.9).toFixed()!=="1",b=Math,a={count:false,inclusive:false,wrap:true};Ext.apply(d,{Clip:{DEFAULT:a,COUNT:Ext.applyIf({count:true},a),INCLUSIVE:Ext.applyIf({inclusive:true},a),NOWRAP:Ext.applyIf({wrap:false},a)},clipIndices:function(m,n,h){h=h||a;var g=0,l=h.wrap,k,e,j;n=n||[];for(j=0;j<2;++j){k=e;e=n[j];if(e==null){e=g}else{if(j&&h.count){e+=k;e=(e>m)?m:e}else{if(l){e=(e<0)?(m+e):e}if(j&&h.inclusive){++e}e=(e<0)?0:((e>m)?m:e)}}g=m}n[0]=k;n[1]=(eg)?g:e)},snap:function(i,g,h,j){var e;if(i===undefined||i=g){i+=g}else{if(e*2<-g){i-=g}}}}return d.constrain(i,h,j)},snapInRange:function(i,e,h,j){var g;h=(h||0);if(i===undefined||i=e){i+=e}}if(j!==undefined){if(i>(j=d.snapInRange(j,e,h))){i=j}}return i},sign:function(e){e=+e;if(e===0||isNaN(e)){return e}return(e>0)?1:-1},toFixed:c?function(h,e){e=e||0;var g=b.pow(10,e);return(b.round(h*g)/g).toFixed(e)}:function(g,e){return g.toFixed(e)},from:function(g,e){if(isFinite(g)){g=parseFloat(g)}return !isNaN(g)?g:e},randomInt:function(g,e){return b.floor(b.random()*(e-g+1)+g)},correctFloat:function(e){return parseFloat(e.toPrecision(14))}});Ext.num=function(){return d.from.apply(this,arguments)}}());(function(){var d=function(){},b=/^\?/,c=/(\[):?([^\]]*)\]/g,a=/^([^\[]+)/,g=/\+/g,e=Ext.Object={chain:Object.create||function(i){d.prototype=i;var h=new d();d.prototype=null;return h},clear:function(h){for(var i in h){delete h[i]}return h},freeze:Object.freeze?function(j,h){if(j&&typeof j==="object"&&!Object.isFrozen(j)){Object.freeze(j);if(h){for(var i in j){e.freeze(j[i],h)}}}return j}:Ext.identityFn,toQueryObjects:function(k,o,j){var h=e.toQueryObjects,n=[],l,m;if(Ext.isArray(o)){for(l=0,m=o.length;l0){o=s.split("=");A=o[0];A=A.replace(g,"%20");A=decodeURIComponent(A);r=o[1];if(r!==undefined){r=r.replace(g,"%20");r=decodeURIComponent(r)}else{r=""}if(!v){if(y.hasOwnProperty(A)){if(!Ext.isArray(y[A])){y[A]=[y[A]]}y[A].push(r)}else{y[A]=r}}else{n=A.match(c);x=A.match(a);A=x[0];p=[];if(n===null){y[A]=r;continue}for(t=0,h=n.length;t@:]*)(?:[@]([^<>@:]+))?[>](.+)$/,resolveResource:function(c){var b=c,a;if(c&&c.charAt(0)==="<"){a=Ext._resourcePoolRe.exec(c);if(a){b=Ext.getResourcePath(a[3],a[1],a[2])}}return b},urlEncode:function(){var a=Ext.Array.from(arguments),b="";if(Ext.isString(a[1])){b=a[1]+"&";a[1]=false}return b+Ext.Object.toQueryString.apply(Ext.Object,a)},urlDecode:function(){return Ext.Object.fromQueryString.apply(Ext.Object,arguments)},getScrollbarSize:function(c){var b=Ext._scrollbarSize;if(c||!b){var a=document.body,d=document.createElement("div");d.style.width=d.style.height="100px";d.style.overflow="scroll";d.style.position="absolute";a.appendChild(d);Ext._scrollbarSize=b={width:d.offsetWidth-d.clientWidth,height:d.offsetHeight-d.clientHeight};a.removeChild(d)}return b},typeOf:(function(){var a=/\S/,c=Object.prototype.toString,d={number:1,string:1,"boolean":1,"undefined":1},b={"[object Array]":"array","[object Date]":"date","[object Boolean]":"boolean","[object Number]":"number","[object RegExp]":"regexp"};return function(h){if(h===null){return"null"}var g=typeof h,e,i;if(d[g]){return g}e=b[i=c.call(h)];if(e){return e}if(g==="function"){return"function"}if(g==="object"){if(h.nodeType!==undefined){if(h.nodeType===3){return a.test(h.nodeValue)?"textnode":"whitespace"}else{return"element"}}return"object"}return i}}()),factory:function(b,e,a,g){var d=Ext.ClassManager,c;if(!b||b.isInstance){if(a&&a!==b){a.destroy()}return b}if(g){if(typeof b==="string"){return d.instantiateByAlias(g+"."+b)}else{if(Ext.isObject(b)&&"type" in b){return d.instantiateByAlias(g+"."+b.type,b)}}}if(b===true){return a||Ext.create(e)}if("xtype" in b){c=d.instantiateByAlias("widget."+b.xtype,b)}else{if("xclass" in b){c=Ext.create(b.xclass,b)}}if(c){if(a){a.destroy()}return c}if(a){return a.setConfig(b)}return Ext.create(e,b)},log:(function(){var a=function(){};a.info=a.warn=a.error=Ext.emptyFn;return a}())});(function(){var d=[""],i=/([^\d\.])/,b=/[^\d]/g,a=/[\-+]/g,h=/\s/g,c=/_/g,g={classic:1,modern:1},e;Ext.Version=e=function(r,n){var s=this,l=s.padModes,j,p,m,o,t,k,q;if(r.isVersion){r=r.version}s.version=q=String(r).toLowerCase().replace(c,".").replace(a,"");j=q.charAt(0);if(j in l){q=q.substring(1);m=l[j]}else{m=n?l[n]:0}s.pad=m;k=q.search(i);s.shortVersion=q;if(k!==-1){s.release=t=q.substr(k,r.length);s.shortVersion=q.substr(0,k);t=e.releaseValueMap[t]||t}s.releaseValue=t||m;s.shortVersion=s.shortVersion.replace(b,"");s.parts=o=q.split(".");for(p=o.length;p--;){o[p]=parseInt(o[p],10)}if(m===Infinity){o.push(m)}s.major=o[0]||m;s.minor=o[1]||m;s.patch=o[2]||m;s.build=o[3]||m;return s};e.prototype={isVersion:true,padModes:{"~":NaN,"^":Infinity},release:"",compareTo:function(t){var u=this,n=u.pad,r=u.parts,v=r.length,m=t.isVersion?t:new e(t),k=m.pad,q=m.parts,p=q.length,j=Math.max(v,p),o,l,s;for(o=0;os){return 1}}l=u.releaseValue;s=m.releaseValue;if(ls){return 1}return 0},toString:function(){return this.version},valueOf:function(){return this.version},getMajor:function(){return this.major},getMinor:function(){return this.minor},getPatch:function(){return this.patch},getBuild:function(){return this.build},getRelease:function(){return this.release},getReleaseValue:function(){return this.releaseValue},isGreaterThan:function(j){return this.compareTo(j)>0},isGreaterThanOrEqual:function(j){return this.compareTo(j)>=0},isLessThan:function(j){return this.compareTo(j)<0},isLessThanOrEqual:function(j){return this.compareTo(j)<=0},equals:function(j){return this.compareTo(j)===0},match:function(j){j=String(j);return this.version.substr(0,j.length)===j},toArray:function(){var j=this;return[j.getMajor(),j.getMinor(),j.getPatch(),j.getBuild(),j.getRelease()]},getShortVersion:function(){return this.shortVersion},gt:function(j){return this.compareTo(j)>0},lt:function(j){return this.compareTo(j)<0},gtEq:function(j){return this.compareTo(j)>=0},ltEq:function(j){return this.compareTo(j)<=0}};Ext.apply(e,{aliases:{from:{extjs:"ext",core:"core",touch:"modern"},to:{ext:["extjs"],core:["core"],modern:["touch"]}},releaseValueMap:{dev:-6,alpha:-5,a:-5,beta:-4,b:-4,rc:-3,"#":-2,p:-1,pl:-1},getComponentValue:function(j){return !j?0:(isNaN(j)?this.releaseValueMap[j]||j:parseInt(j,10))},compare:function(l,k){var j=l.isVersion?l:new e(l);return j.compareTo(k)},set:function(o,m,l){var k=e.aliases.to[m],j=l.isVersion?l:new e(l),n;o[m]=j;if(k){for(n=k.length;n-->0;){o[k[n]]=j}}return j}});Ext.apply(Ext,{compatVersions:{},versions:{},lastRegisteredVersion:null,getCompatVersion:function(k){var j=Ext.compatVersions,l;if(!k){l=j.ext||j.touch||j.core}else{l=j[e.aliases.from[k]||k]}return l||Ext.getVersion(k)},setCompatVersion:function(k,j){e.set(Ext.compatVersions,k,j)},setVersion:function(k,j){if(k in g){Ext.toolkit=k}Ext.lastRegisteredVersion=e.set(Ext.versions,k,j);return this},getVersion:function(k){var j=Ext.versions;if(!k){return j.ext||j.touch||j.core}return j[e.aliases.from[k]||k]},checkVersion:function(p,x){var t=Ext.isArray(p),l=e.aliases.from,y=t?p:d,k=y.length,m=Ext.versions,w=m.ext||m.touch,q,v,s,n,o,j,z,r,u;if(!t){d[0]=p}for(q=0;q=0){z=z.replace(h,"")}v=z.indexOf("@");if(v<0){r=z;u=w}else{j=z.substring(0,v);if(!(u=m[l[j]||j])){if(x){return false}continue}r=z.substring(v+1)}v=r.indexOf("-");if(v<0){if(r.charAt(v=r.length-1)==="+"){n=r.substring(0,v);o=null}else{n=o=r}}else{if(v>0){n=r.substring(0,v);o=r.substring(v+1)}else{n=null;o=r.substring(v+1)}}s=true;if(n){n=new e(n,"~");s=n.ltEq(u)}if(s&&o){o=new e(o,"~");s=o.gtEq(u)}}if(s){if(!x){return true}}else{if(x){return false}}}return !!x},deprecate:function(j,l,m,k){if(e.compare(Ext.getVersion(j),l)<1){m.call(k)}}})}());(function(d){var e=(d&&d.packages)||{},c=d&&d.compatibility,b,a;for(b in e){a=e[b];Ext.setVersion(b,a.version)}if(c){if(Ext.isString(c)){Ext.setCompatVersion("core",c)}else{for(b in c){Ext.setCompatVersion(b,c[b])}}}if(!e.ext&&!e.touch){Ext.setVersion("ext","6.0.1.250");Ext.setVersion("core","6.0.1.250")}})(Ext.manifest);Ext.Config=function(b){var c=this,a=b.charAt(0).toUpperCase()+b.substr(1);c.name=b;c.names={internal:"_"+b,initializing:"is"+a+"Initializing",apply:"apply"+a,update:"update"+a,get:"get"+a,set:"set"+a,initGet:"initGet"+a,changeEvent:b.toLowerCase()+"change"};c.root=c};Ext.Config.map={};Ext.Config.get=function(b){var c=Ext.Config.map,a=c[b]||(c[b]=new Ext.Config(b));return a};Ext.Config.prototype={self:Ext.Config,isConfig:true,getGetter:function(){return this.getter||(this.root.getter=this.makeGetter())},getInitGetter:function(){return this.initGetter||(this.root.initGetter=this.makeInitGetter())},getSetter:function(){return this.setter||(this.root.setter=this.makeSetter())},getEventedSetter:function(){return this.eventedSetter||(this.root.eventedSetter=this.makeEventedSetter())},getInternalName:function(a){return a.$configPrefixed?this.names.internal:this.name},mergeNew:function(g,b,e,d){var a,c;if(!b){a=g}else{if(!g){a=b}else{a=Ext.Object.chain(b);for(c in g){if(!d||!(c in a)){a[c]=g[c]}}}}return a},mergeSets:function(e,c,a){var b=c?Ext.Object.chain(c):{},d,g;if(e instanceof Array){for(d=e.length;d--;){g=e[d];if(!a||!(g in b)){b[g]=true}}}else{if(e){if(e.constructor===Object){for(d in e){g=e[d];if(!a||!(d in b)){b[d]=g}}}else{if(!a||!(e in b)){b[e]=true}}}}return b},makeGetter:function(){var a=this.name,b=this.names.internal;return function(){var c=this.$configPrefixed?b:a;return this[c]}},makeInitGetter:function(){var a=this.name,e=this.names,d=e.set,b=e.get,c=e.initializing;return function(){var g=this;g[c]=true;delete g[b];g[d](g.config[a]);delete g[c];return g[b].apply(g,arguments)}},makeSetter:function(){var a=this.name,e=this.names,c=e.internal,d=e.get,b=e.apply,h=e.update,g;g=function(l){var k=this,j=k.$configPrefixed?c:a,i=k[j];delete k[d];if(!k[b]||(l=k[b](l,i))!==undefined){if(l!==(i=k[j])){k[j]=l;if(k[h]){k[h](l,i)}}}return k};g.$isDefault=true;return g},makeEventedSetter:function(){var b=this.name,h=this.names,j=h.internal,a=h.get,i=h.apply,d=h.update,g=h.changeEvent,e=function(m,n,k,l){m[l]=n;if(m[d]){m[d](n,k)}},c;c=function(n){var m=this,l=m.$configPrefixed?j:b,k=m[l];delete m[a];if(!m[i]||(n=m[i](n,k))!==undefined){if(n!==(k=m[l])){if(m.isConfiguring){m[l]=n;if(m[d]){m[d](n,k)}}else{m.fireEventedAction(g,[m,n,k],e,m,[m,n,k,l])}}}return m};c.$isDefault=true;return c}};(function(){var b=Ext.Config,c=b.map,a=Ext.Object;Ext.Configurator=function(d){var g=this,e=d.prototype,h=d.superclass?d.superclass.self.$config:null;g.cls=d;g.superCfg=h;if(h){g.configs=a.chain(h.configs);g.cachedConfigs=a.chain(h.cachedConfigs);g.initMap=a.chain(h.initMap);g.values=a.chain(h.values);g.needsFork=h.needsFork}else{g.configs={};g.cachedConfigs={};g.initMap={};g.values={}}e.config=e.defaultConfig=g.values;d.$config=g};Ext.Configurator.prototype={self:Ext.Configurator,needsFork:false,initList:null,add:function(u,d){var v=this,i=v.cls,l=v.configs,w=v.cachedConfigs,n=v.initMap,q=i.prototype,x=d&&d.$config.configs,e=v.values,k,m,t,g,h,j,y,p,o,r;for(y in u){r=u[y];k=r&&r.constructor===Object;m=k&&"$value" in r?r:null;if(m){t=!!m.cached;r=m.$value;k=r&&r.constructor===Object}g=m&&m.merge;h=l[y];if(h){if(d){g=h.merge;if(!g){continue}m=null}else{g=g||h.merge}j=e[y];if(g){r=g.call(h,r,j,i,d)}else{if(k){if(j&&j.constructor===Object){r=a.merge({},j,r)}}}}else{if(x){h=x[y];m=null}else{h=b.get(y)}l[y]=h;if(h.cached||t){w[y]=true}p=h.names;if(!q[o=p.get]){q[o]=h.getter||h.getGetter()}if(!q[o=p.set]){q[o]=(m&&m.evented)?(h.eventedSetter||h.getEventedSetter()):(h.setter||h.getSetter())}}if(m){if(h.owner!==i){l[y]=h=Ext.Object.chain(h);h.owner=i}Ext.apply(h,m);delete h.$value}if(!v.needsFork&&r&&(r.constructor===Object||r instanceof Array)){v.needsFork=true}if(r!==null){n[y]=true}else{if(q.$configPrefixed){q[l[y].names.internal]=null}else{q[l[y].name]=null}if(y in n){n[y]=false}}e[y]=r}},configure:function(y,m){var A=this,l=A.configs,n=A.initMap,p=A.initListMap,w=A.initList,q=A.cls.prototype,e=A.values,r=0,t=!w,g,h,j,C,v,u,k,o,B,s,z,x,d;e=A.needsFork?a.fork(e):a.chain(e);y.isConfiguring=true;if(t){A.initList=w=[];A.initListMap=p={};y.isFirstInstance=true;for(B in n){C=n[B];h=l[B];z=h.cached;if(C){o=h.names;s=e[B];if(!q[o.set].$isDefault||q[o.apply]||q[o.update]||typeof s==="object"){if(z){(g||(g=[])).push(h)}else{w.push(h);p[B]=true}y[o.get]=h.initGetter||h.getInitGetter()}else{q[h.getInternalName(q)]=s}}else{if(z){q[h.getInternalName(q)]=undefined}}}}k=g&&g.length;if(k){for(v=0;v0){for(n=0;ng.maxSize){g.unlinkEntry(c.prev,true);--g.count}}return e.value},evict:Ext.emptyFn,linkEntry:function(d){var c=this.head,e=c.next;d.next=e;d.prev=c;c.next=d;e.prev=d},unlinkEntry:function(e,g){var c=e.next,d=e.prev;d.next=c;c.prev=d;if(g){this.evict(e.key,e.value)}}};a.destroy=a.clear}());(function(){var d,c=Ext.Base,e=c.$staticMembers,b=function(h,g){return(h.length-g.length)||((hg)?1:0))};function a(h){function g(){return this.constructor.apply(this,arguments)||null}return g}Ext.Class=d=function(h,i,g){if(typeof h!="function"){g=i;i=h;h=null}if(!i){i={}}h=d.create(h,i);d.process(h,i,g);return h};Ext.apply(d,{makeCtor:a,onBeforeCreated:function(h,i,g){h.addMembers(i);g.onCreated.call(h,h)},create:function(g,k){var j=e.length,h;if(!g){g=a()}while(j--){h=e[j];g[h]=c[h]}return g},process:function(g,o,k){var h=o.preprocessors||d.defaultPreprocessors,r=this.preprocessors,u={onBeforeCreated:this.onBeforeCreated},t=[],v,n,m,s,l,q,p;delete o.preprocessors;g._classHooks=u;for(m=0,s=h.length;m0){k=g.test(r[o])}r=d[w];if(r&&!k){o=r.length;while(!k&&o-->0){k=g.test(r[o])}}}if(k){u[w]=1;l.push(w)}}}}}return l},getPath:function(b){var c=this,e=c.paths,a="",d;if(b in e){a=e[b]}else{d=c.getPrefix(b);if(d){b=b.substring(d.length+1);a=e[d];if(a){a+="/"}}a+=b.replace(c.dotRe,"/")+".js"}return a},getPrefix:function(b){if(b in this.paths){return b}var e=this.getPrefixes(),a=e.length,c,d;while(a-->0){c=(d=e[a]).length;if(c0){A=h[u];v=w;w=A.value||w[A.name];if(!w&&z){v[A.name]=w={}}}return w},setNamespace:function(v,x){var w=c.getNamespaceEntry(v),u=Ext.global;if(w.parent){u=c.lookupName(w.parent,true)}u[w.name]=x;return x},setXType:function(u,B){var w=u.$className,A=w?u:c.get(w=u),x=A.prototype,y=x.xtypes,v=x.xtypesChain,z=x.xtypesMap;if(!x.hasOwnProperty("xtypes")){x.xtypes=y=[];x.xtypesChain=v=v?v.slice(0):[];x.xtypesMap=z=Ext.apply({},z)}c.addAlias(w,"widget."+B,true);y.push(B);v.push(B);z[B]=true},set:function(u,w){var v=c.getName(w);c.classes[u]=c.setNamespace(u,w);if(v&&v!==u){c.addAlternate(v,u)}return c},get:function(u){return c.classes[u]||c.lookupName(u,false)},addNameAliasMappings:function(u){c.addAlias(u)},addNameAlternateMappings:function(u){c.addAlternate(u)},getByAlias:function(u){return c.get(c.getNameByAlias(u))},getByConfig:function(v,w){var x=v.xclass,u;if(x){u=x}else{u=v.xtype;if(u){w="widget."}else{u=v.type}u=c.getNameByAlias(w+u)}return c.get(u)},getName:function(u){return u&&u.$className||""},getClass:function(u){return u&&u.self||null},create:function(v,x,u){var w=b(v);if(typeof x==="function"){x=x(w)}x.$className=v;return new q(w,x,function(){var y=x.postprocessors||c.defaultPostprocessors,F=c.postprocessors,G=[],E,A,D,z,C,B,H;delete x.postprocessors;for(A=0,D=y.length;A0;){u=c.lookupName(arguments[v],true)}return u}});Ext.addRootNamespaces=c.addRootNamespaces;Ext.createWidget=Ext.widget;Ext.ns=Ext.namespace;q.registerPreprocessor("className",function(u,v){if("$className" in v){u.$className=v.$className}},true,"first");q.registerPreprocessor("alias",function(F,z){var D=F.prototype,w=e(z.xtype),u=e(z.alias),G="widget.",E=G.length,A=Array.prototype.slice.call(D.xtypesChain||[]),x=Ext.merge({},D.xtypesMap||{}),y,C,B,v;for(y=0,C=u.length;y=p){Ext[n+"p"]=true}}}if(v.is.Opera&&parseInt(d,10)<=12){Ext.isOpera12m=true}Ext.chromeVersion=Ext.isChrome?d:0;Ext.firefoxVersion=Ext.isFirefox?d:0;Ext.ieVersion=Ext.isIE?d:0;Ext.operaVersion=Ext.isOpera?d:0;Ext.safariVersion=Ext.isSafari?d:0;Ext.webKitVersion=Ext.isWebKit?d:0;this.setFlag(q+d,true,o);this.setFlag(q+m.getShortVersion())}for(p in c){if(c.hasOwnProperty(p)){w=c[p];this.setFlag(w,q===w)}}this.setFlag(w);if(g){this.setFlag(t+(g.getMajor()||""));this.setFlag(t+g.getShortVersion())}for(p in k){if(k.hasOwnProperty(p)){w=k[p];this.setFlag(w,t===w,o)}}this.setFlag("Standalone",!!navigator.standalone);this.setFlag("Ripple",!!document.getElementById("tinyhippos-injected")&&!Ext.isEmpty(window.top.ripple));this.setFlag("WebWorks",!!window.blackberry);if(window.PhoneGap!==undefined||window.Cordova!==undefined||window.cordova!==undefined){j=true;this.setFlag("PhoneGap");this.setFlag("Cordova")}if(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)(?!.*FBAN)/i.test(u)){j=true}this.setFlag("WebView",j);this.isStrict=Ext.isStrict=document.compatMode==="CSS1Compat";this.isSecure=Ext.isSecure;this.identity=q+d+(this.isStrict?"Strict":"Quirks")};Ext.env.Browser.prototype={constructor:Ext.env.Browser,engineNames:{webkit:"WebKit",gecko:"Gecko",presto:"Presto",trident:"Trident",other:"Other"},enginePrefixes:{webkit:"AppleWebKit/",gecko:"Gecko/",presto:"Presto/",trident:"Trident/"},styleDashPrefixes:{WebKit:"-webkit-",Gecko:"-moz-",Trident:"-ms-",Presto:"-o-",Other:""},stylePrefixes:{WebKit:"Webkit",Gecko:"Moz",Trident:"ms",Presto:"O",Other:""},propertyPrefixes:{WebKit:"webkit",Gecko:"moz",Trident:"ms",Presto:"o",Other:""},is:function(a){return !!this.is[a]},name:null,version:null,engineName:null,engineVersion:null,setFlag:function(a,c,b){if(c===undefined){c=true}this.is[a]=c;this.is[a.toLowerCase()]=c;if(b){Ext["is"+a]=c}return this},getStyleDashPrefix:function(){return this.styleDashPrefixes[this.engineName]},getStylePrefix:function(){return this.stylePrefixes[this.engineName]},getVendorProperyName:function(a){var b=this.propertyPrefixes[this.engineName];if(b.length>0){return b+Ext.String.capitalize(a)}return a},getPreferredTranslationMethod:function(a){if(typeof a==="object"&&"translationMethod" in a&&a.translationMethod!=="auto"){return a.translationMethod}else{return"csstransform"}}};(function(a){Ext.browser=new Ext.env.Browser(a,true);Ext.userAgent=a.toLowerCase();Ext.SSL_SECURE_URL=Ext.isSecure&&Ext.isIE?"javascript:''":"about:blank"}(Ext.global.navigator.userAgent));Ext.env.OS=function(p,b,m){var l=this,k=Ext.Boot.osNames,d=Ext.Boot.osPrefixes,a,j="",g=l.is,c,h,e,o,n;m=m||Ext.browser;for(c in d){if(d.hasOwnProperty(c)){h=d[c];e=p.match(new RegExp("(?:"+h+")([^\\s;]+)"));if(e){a=k[c];n=e[1];if(n&&n==="HTC_"){j=new Ext.Version("2.3")}else{if(n&&n==="Silk/"){j=new Ext.Version("2.3")}else{j=new Ext.Version(e[e.length-1])}}break}}}if(!a){a=k[(p.toLowerCase().match(/mac|win|linux/)||["other"])[0]];j=new Ext.Version("")}this.name=a;this.version=j;if(b){this.setFlag(b.replace(/ simulator$/i,""))}this.setFlag(a);if(j){this.setFlag(a+(j.getMajor()||""));this.setFlag(a+j.getShortVersion())}for(c in k){if(k.hasOwnProperty(c)){o=k[c];if(!g.hasOwnProperty(a)){this.setFlag(o,(a===o))}}}if(this.name==="iOS"&&window.screen.height===568){this.setFlag("iPhone5")}if(m.is.Safari||m.is.Silk){if(this.is.Android2||this.is.Android3||m.version.shortVersion===501){m.setFlag("AndroidStock")}if(this.is.Android4){m.setFlag("AndroidStock");m.setFlag("AndroidStock4")}}};Ext.env.OS.prototype={constructor:Ext.env.OS,is:function(a){return !!this[a]},name:null,version:null,setFlag:function(a,b){if(b===undefined){b=true}if(this.flags){this.flags[a]=b}this.is[a]=b;this.is[a.toLowerCase()]=b;return this}};(function(){var a=Ext.global.navigator,h=a.userAgent,g=Ext.env.OS,e=(Ext.is||(Ext.is={})),i,d,b;g.prototype.flags=e;Ext.os=i=new g(h,a.platform);d=i.name;Ext["is"+d]=true;Ext.isMac=e.Mac=e.MacOS;var j=window.location.search.match(/deviceType=(Tablet|Phone)/),c=window.deviceType;if(j&&j[1]){b=j[1]}else{if(c==="iPhone"){b="Phone"}else{if(c==="iPad"){b="Tablet"}else{if(!i.is.Android&&!i.is.iOS&&!i.is.WindowsPhone&&/Windows|Linux|MacOS/.test(d)){b="Desktop";Ext.browser.is.WebView=!!Ext.browser.is.Ripple}else{if(i.is.iPad||i.is.RIMTablet||i.is.Android3||Ext.browser.is.Silk||(i.is.Android&&h.search(/mobile/i)===-1)){b="Tablet"}else{b="Phone"}}}}}i.setFlag(b,true);i.deviceType=b;delete g.prototype.flags}());Ext.feature={has:function(a){return !!this.has[a]},testElements:{},getTestElement:function(a,b){if(a===undefined){a="div"}else{if(typeof a!=="string"){return a}}if(b){return document.createElement(a)}if(!this.testElements[a]){this.testElements[a]=document.createElement(a)}return this.testElements[a]},isStyleSupported:function(c,b){var d=this.getTestElement(b).style,a=Ext.String.capitalize(c);if(typeof d[c]!=="undefined"||typeof d[Ext.browser.getStylePrefix(c)+a]!=="undefined"){return true}return false},isStyleSupportedWithoutPrefix:function(b,a){var c=this.getTestElement(a).style;if(typeof c[b]!=="undefined"){return true}return false},isEventSupported:function(c,a){if(a===undefined){a=window}var e=this.getTestElement(a),b="on"+c.toLowerCase(),d=(b in e);if(!d){if(e.setAttribute&&e.removeAttribute){e.setAttribute(b,"");d=typeof e[b]==="function";if(typeof e[b]!=="undefined"){e[b]=undefined}e.removeAttribute(b)}}return d},getStyle:function(c,b){var a=c.ownerDocument.defaultView,d=(a?a.getComputedStyle(c,null):c.currentStyle);return(d||c.style)[b]},getSupportedPropertyName:function(b,a){var c=Ext.browser.getVendorProperyName(a);if(c in b){return c}else{if(a in b){return a}}return null},detect:function(j){var k=this,m=document,g=k.toRun||k.tests,e=g.length,b=m.createElement("div"),c=[],p=Ext.supports,o=k.has,a,i,h,d,l;b.innerHTML='
    ';if(j){m.body.appendChild(b)}d=k.preDetected[Ext.browser.identity]||[];while(e--){h=g[e];l=d[e];a=h.name;i=h.names;if(l===undefined){if(!j&&h.ready){c.push(h);continue}l=h.fn.call(k,m,b)}if(a){p[a]=o[a]=l}else{if(i){while(i.length){a=i.pop();p[a]=o[a]=l}}}}if(j){m.body.removeChild(b)}k.toRun=c},report:function(){var b=[],a=this.tests.length,c;for(c=0;c
    ";a=(b.childNodes.length===1);b.innerHTML="";return a}},{name:"touchScroll",fn:function(){var a=0;if(Ext.os.is.Desktop&&(navigator.maxTouchPoints||navigator.msMaxTouchPoints)){a=1}else{if(Ext.supports.Touch){a=2}}return a}},{name:"Touch",fn:function(){var a=navigator.msMaxTouchPoints||navigator.maxTouchPoints;if(Ext.browser.is.Chrome&&Ext.browser.version.isLessThanOrEqual(39)){return(Ext.supports.TouchEvents&&a!==1)||a>1}else{return Ext.supports.TouchEvents||a>0}}},{name:"TouchEvents",fn:function(){return this.isEventSupported("touchend")}},{name:"PointerEvents",fn:function(){return navigator.pointerEnabled}},{name:"MSPointerEvents",fn:function(){return navigator.msPointerEnabled}},{name:"Orientation",fn:function(){return("orientation" in window)&&this.isEventSupported("orientationchange")}},{name:"OrientationChange",fn:function(){return this.isEventSupported("orientationchange")}},{name:"DeviceMotion",fn:function(){return this.isEventSupported("devicemotion")}},{names:["Geolocation","GeoLocation"],fn:function(){return"geolocation" in window.navigator}},{name:"SqlDatabase",fn:function(){return"openDatabase" in window}},{name:"WebSockets",fn:function(){return"WebSocket" in window}},{name:"Range",fn:function(){return !!document.createRange}},{name:"CreateContextualFragment",fn:function(){var a=!!document.createRange?document.createRange():false;return a&&!!a.createContextualFragment}},{name:"History",fn:function(){return("history" in window&&"pushState" in window.history)}},{name:"Css3dTransforms",fn:function(){return this.has("CssTransforms")&&this.isStyleSupported("perspective")}},{name:"CssTransforms",fn:function(){return this.isStyleSupported("transform")}},{name:"CssTransformNoPrefix",fn:function(){return this.isStyleSupportedWithoutPrefix("transform")}},{name:"CssAnimations",fn:function(){return this.isStyleSupported("animationName")}},{names:["CssTransitions","Transitions"],fn:function(){return this.isStyleSupported("transitionProperty")}},{names:["Audio","AudioTag"],fn:function(){return !!this.getTestElement("audio").canPlayType}},{name:"Video",fn:function(){return !!this.getTestElement("video").canPlayType}},{name:"LocalStorage",fn:function(){try{if("localStorage" in window&&window.localStorage!==null){localStorage.setItem("sencha-localstorage-test","test success");localStorage.removeItem("sencha-localstorage-test");return true}}catch(a){}return false}},{name:"XHR2",fn:function(){return window.ProgressEvent&&window.FormData&&window.XMLHttpRequest&&("withCredentials" in new XMLHttpRequest())}},{name:"XHRUploadProgress",fn:function(){if(window.XMLHttpRequest&&!Ext.browser.is.AndroidStock){var a=new XMLHttpRequest();return a&&("upload" in a)&&("onprogress" in a.upload)}return false}},{name:"NumericInputPlaceHolder",fn:function(){return !(Ext.browser.is.AndroidStock4&&Ext.os.version.getMinor()<2)}},{name:"matchesSelector",fn:function(){var b=document.documentElement,e="matches",d="webkitMatchesSelector",a="msMatchesSelector",c="mozMatchesSelector";return b[e]?e:b[d]?d:b[a]?a:b[c]?c:null}},{name:"RightMargin",ready:true,fn:function(b,c){var a=b.defaultView;return !(a&&a.getComputedStyle(c.firstChild.firstChild,null).marginRight!=="0px")}},{name:"DisplayChangeInputSelectionBug",fn:function(){var a=Ext.webKitVersion;return 0a";b=a.firstChild;a.innerHTML="
    b
    ";return b.innerHTML!=="a"}},{name:"IncludePaddingInWidthCalculation",ready:true,fn:function(a,b){return b.childNodes[1].firstChild.offsetWidth===210}},{name:"IncludePaddingInHeightCalculation",ready:true,fn:function(a,b){return b.childNodes[1].firstChild.offsetHeight===210}},{name:"TextAreaMaxLength",fn:function(a){return("maxlength" in a.createElement("textarea"))}},{name:"GetPositionPercentage",ready:true,fn:function(a,b){return Ext.feature.getStyle(b.childNodes[2],"left")==="10%"}},{name:"PercentageHeightOverflowBug",ready:true,fn:function(d){var a=false,c,b;if(Ext.getScrollbarSize().height){b=this.getTestElement();c=b.style;c.height="50px";c.width="50px";c.overflow="auto";c.position="absolute";b.innerHTML=['
    ','
    ',"
    "].join("");d.body.appendChild(b);if(b.firstChild.offsetHeight===50){a=true}d.body.removeChild(b)}return a}},{name:"xOriginBug",ready:true,fn:function(d,e){e.innerHTML='
    ';var c=document.getElementById("b1").getBoundingClientRect(),b=document.getElementById("b2").getBoundingClientRect(),a=document.getElementById("b3").getBoundingClientRect();return(b.left!==c.left&&a.right!==c.right)}},{name:"ScrollWidthInlinePaddingBug",ready:true,fn:function(d){var a=false,c,b;b=d.createElement("div");c=b.style;c.height="50px";c.width="50px";c.padding="10px";c.overflow="hidden";c.position="absolute";b.innerHTML='';d.body.appendChild(b);if(b.scrollWidth===70){a=true}d.body.removeChild(b);return a}},{name:"rtlVertScrollbarOnRight",ready:true,fn:function(c,d){d.innerHTML='
    ';var b=d.firstChild,a=b.firstChild;return(a.offsetLeft+a.offsetWidth!==b.offsetLeft+b.offsetWidth)}},{name:"rtlVertScrollbarOverflowBug",ready:true,fn:function(b,c){c.innerHTML='
    ';var a=c.firstChild;return a.clientHeight===a.offsetHeight}},{identity:"defineProperty",fn:function(){if(Ext.isIE8m){Ext.Object.defineProperty=Ext.emptyFn;return false}return true}},{identify:"nativeXhr",fn:function(){if(typeof XMLHttpRequest!=="undefined"){return true}XMLHttpRequest=function(){try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(a){return null}};return false}},{name:"SpecialKeyDownRepeat",fn:function(){return Ext.isWebKit?parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1],10)>=525:!((Ext.isGecko&&!Ext.isWindows)||(Ext.isOpera&&Ext.operaVersion<12))}},{name:"EmulatedMouseOver",fn:function(){return Ext.os.is.iOS}},{name:"Hashchange",fn:function(){var a=document.documentMode;return"onhashchange" in window&&(a===undefined||a>7)}},{name:"FixedTableWidthBug",ready:true,fn:function(){if(Ext.isIE8){return false}var b=document.createElement("div"),a=document.createElement("div"),c;b.setAttribute("style","display:table;table-layout:fixed;");a.setAttribute("style","display:table-cell;min-width:50px;");b.appendChild(a);document.body.appendChild(b);b.offsetWidth;b.style.width="25px";c=b.offsetWidth;document.body.removeChild(b);return c===50}},{name:"FocusinFocusoutEvents",fn:function(){return !Ext.isGecko}},{name:"AsyncFocusEvents",fn:function(){return Ext.asyncFocus=!!Ext.isIE}},{name:"accessibility",ready:true,fn:function(h){var a=h.body,i,d,e,b,c;function g(o){var j=[],l=0,n,k;if(o.indexOf("rgb(")!==-1){j=o.replace("rgb(","").replace(")","").split(", ")}else{if(o.indexOf("#")!==-1){n=o.length===7?/^#(\S\S)(\S\S)(\S\S)$/:/^#(\S)(\S)(\S)$/;k=o.match(n);if(k){j=["0x"+k[1],"0x"+k[2],"0x"+k[3]]}}}for(var m=0;m0};Ext.supports.HighContrastMode=!b.BackgroundImages;a.removeChild(i);i=d=null;return b}},0]};Ext.feature.tests.pop();Ext.supports={};Ext.feature.detect();Ext.env.Ready={blocks:(location.search||"").indexOf("ext-pauseReadyFire")>0?1:0,bound:0,delay:1,firing:false,generation:0,listeners:[],nextId:0,sortGeneration:0,state:0,timer:null,bind:function(){var a=Ext.env.Ready,b=document;if(!a.bound){if(b.readyState==="complete"){a.onReadyEvent({type:b.readyState||"body"})}else{a.bound=1;if(Ext.browser.is.PhoneGap&&!Ext.os.is.Desktop){a.bound=2;b.addEventListener("deviceready",a.onReadyEvent,false)}b.addEventListener("DOMContentLoaded",a.onReadyEvent,false);window.addEventListener("load",a.onReadyEvent,false)}}},block:function(){++this.blocks;Ext.isReady=false},fireReady:function(){var a=Ext.env.Ready;if(!a.state){Ext._readyTime=Ext.ticks();Ext.isDomReady=true;a.state=1;Ext.feature.detect(true);if(!a.delay){a.handleReady()}else{if(navigator.standalone){a.timer=Ext.defer(function(){a.timer=null;a.handleReadySoon()},1)}else{a.handleReadySoon()}}}},handleReady:function(){var a=this;if(a.state===1){a.state=2;Ext._beforeReadyTime=Ext.ticks();a.invokeAll();Ext._afterReadyTime=Ext.ticks()}},handleReadySoon:function(a){var b=this;if(!b.timer){b.timer=Ext.defer(function(){b.timer=null;b.handleReady()},a||b.delay)}},invoke:function(b){var a=b.delay;if(a){Ext.defer(b.fn,a,b.scope)}else{if(Ext.elevateFunction){Ext.elevateFunction(b.fn,b.scope)}else{b.fn.call(b.scope)}}},invokeAll:function(){if(Ext.elevateFunction){Ext.elevateFunction(this.doInvokeAll,this)}else{this.doInvokeAll()}},doInvokeAll:function(){var b=this,a=b.listeners,c;if(!b.blocks){Ext.isReady=true}b.firing=true;while(a.length){if(b.sortGeneration!==b.generation){b.sortGeneration=b.generation;a.sort(b.sortFn)}c=a.pop();if(b.blocks&&!c.dom){a.push(c);break}b.invoke(c)}b.firing=false},makeListener:function(d,c,b){var a={fn:d,id:++this.nextId,scope:c,dom:false,priority:0};if(b){Ext.apply(a,b)}a.phase=a.dom?0:1;return a},on:function(c,b,a){var d=Ext.env.Ready,e=d.makeListener(c,b,a);if(d.state===2&&!d.firing&&(e.dom||!d.blocks)){d.invoke(e)}else{d.listeners.push(e);++d.generation;if(!d.bound){d.bind()}}},onReadyEvent:function(b){var a=Ext.env.Ready;if(Ext.elevateFunction){Ext.elevateFunction(a.doReadyEvent,a,arguments)}else{a.doReadyEvent(b)}},doReadyEvent:function(b){var a=this;if(a.bound>0){a.unbind();a.bound=-1}if(!a.state){a.fireReady()}},sortFn:function(d,c){return -((d.phase-c.phase)||(c.priority-d.priority)||(d.id-c.id))},unblock:function(){var a=this;if(a.blocks){if(!--a.blocks){if(a.state===2&&!a.firing){a.invokeAll()}}}},unbind:function(){var a=this,b=document;if(a.bound>1){b.removeEventListener("deviceready",a.onReadyEvent,false)}b.removeEventListener("DOMContentLoaded",a.onReadyEvent,false);window.removeEventListener("load",a.onReadyEvent,false)}};(function(){var a=Ext.env.Ready;if(Ext.isIE9m){Ext.apply(a,{scrollTimer:null,readyStatesRe:/complete/i,pollScroll:function(){var b=true;try{document.documentElement.doScroll("left")}catch(c){b=false}if(b&&document.body){a.onReadyEvent({type:"doScroll"})}else{a.scrollTimer=Ext.defer(a.pollScroll,20)}return b},bind:function(){if(a.bound){return}var d=document,b;try{b=window.frameElement===undefined}catch(c){}if(!b||!d.documentElement.doScroll){a.pollScroll=Ext.emptyFn}else{if(a.pollScroll()){return}}if(d.readyState==="complete"){a.onReadyEvent({type:"already "+(d.readyState||"body")})}else{d.attachEvent("onreadystatechange",a.onReadyStateChange);window.attachEvent("onload",a.onReadyEvent);a.bound=1}},unbind:function(){document.detachEvent("onreadystatechange",a.onReadyStateChange);window.detachEvent("onload",a.onReadyEvent);if(Ext.isNumber(a.scrollTimer)){clearTimeout(a.scrollTimer);a.scrollTimer=null}},onReadyStateChange:function(){var b=document.readyState;if(a.readyStatesRe.test(b)){a.onReadyEvent({type:b})}}})}Ext.onDocumentReady=function(e,d,b){var c={dom:true};if(b){Ext.apply(c,b)}a.on(e,d,c)};Ext.onReady=function(d,c,b){a.on(d,c,b)};Ext.onInternalReady=function(d,c,b){a.on(d,c,Ext.apply({priority:1000},b))};a.bind()}());Ext.Loader=(new function(){var c=this,a=Ext.ClassManager,h=Ext.Boot,d=Ext.Class,j=Ext.env.Ready,i=Ext.Function.alias,g=["extend","mixins","requires"],n={},k=[],b=[],e=[],o={},m={},l={enabled:true,scriptChainDelay:false,disableCaching:true,disableCachingParam:"_dc",paths:a.paths,preserveScripts:true,scriptCharset:undefined},p={disableCaching:true,disableCachingParam:true,preserveScripts:true,scriptChainDelay:"loadDelay"};Ext.apply(c,{isInHistory:n,isLoading:false,history:k,config:l,readyListeners:b,optionalRequires:e,requiresMap:o,hasFileLoadError:false,scriptsLoading:0,syncModeEnabled:false,missingQueue:m,init:function(){var v=document.getElementsByTagName("script"),q=v[v.length-1].src,A=q.substring(0,q.lastIndexOf("/")+1),y=Ext._classPathMetadata,z=Ext.Microloader,s=Ext.manifest,t,w,x,u,r;if(!a.getPath("Ext")){a.setPath("Ext",A+"src")}if(y){Ext._classPathMetadata=null;c.addClassPathMappings(y)}if(s){t=s.loadOrder;w=Ext.Boot.baseUrl;if(t&&s.bootRelative){for(x=t.length,u=0;u1)?"es":"")+": "+t.join(", "))}if(x.length){c.loadScripts({url:x,_classNames:t})}else{c.checkReady()}}else{if(y){y.call(z)}c.checkReady()}if(c.syncModeEnabled){if(s===1){return a.get(q[0])}}return c},makeLoadCallback:function(q,r){return function(){var t=[],s=q.length;while(s-->0){t[s]=a.get(q[s])}return r.apply(this,t)}},onLoadFailure:function(){var q=this,r=q.onError;c.hasFileLoadError=true;--c.scriptsLoading;if(r){r.call(q.userScope,q)}c.checkReady()},onLoadSuccess:function(){var q=this,r=q.onLoad;--c.scriptsLoading;if(r){r.call(q.userScope,q)}c.checkReady()},onReady:function(s,r,u,q){if(u){j.on(s,r,q)}else{var t=j.makeListener(s,r,q);if(c.isLoading){b.push(t)}else{j.invoke(t)}}},addUsedClasses:function(s){var q,r,t;if(s){s=(typeof s==="string")?[s]:s;for(r=0,t=s.length;r0){c.loadScripts({url:u,sequential:true})}}}if(w.uses){u=w.uses;c.addUsedClasses(u)}});a.onCreated(c.historyPush);c.init()}());Ext._endTime=Ext.ticks();if(Ext._beforereadyhandler){Ext._beforereadyhandler()}(Ext.cmd.derive("Ext.Mixin",Ext.Base,function(a){return{statics:{addHook:function(i,e,c,d){var h=Ext.isFunction(i),g=function(){var k=arguments,l=h?i:d[i],j=this.callParent(k);l.apply(this,k);return j},b=e.hasOwnProperty(c)&&e[c];if(h){i.$previous=Ext.emptyFn}g.$name=c;g.$owner=e.self;if(b){g.$previous=b.$previous;b.$previous=g}else{e[c]=g}}},onClassExtended:function(l,d){var g=d.mixinConfig,j=d.xhooks,i=l.superclass,e=d.onClassMixedIn,b,h,k,c;if(j){delete d.xhooks;(g||(d.mixinConfig=g={})).on=j}if(g){b=i.mixinConfig;if(b){d.mixinConfig=g=Ext.merge({},b,g)}d.mixinId=g.id;h=g.before;k=g.after;j=g.on;c=g.extended}if(h||k||j||c){d.onClassMixedIn=function(p){var m=this.prototype,o=p.prototype,n;if(h){Ext.Object.each(h,function(q,r){p.addMember(q,function(){if(m[r].apply(this,arguments)!==false){return this.callParent(arguments)}})})}if(k){Ext.Object.each(k,function(q,r){p.addMember(q,function(){var s=this.callParent(arguments);m[r].apply(this,arguments);return s})})}if(j){for(n in j){a.addHook(j[n],o,n,m)}}if(c){p.onExtended(function(){var q=Ext.Array.slice(arguments,0);q.unshift(p);return c.apply(this,q)},this)}if(e){e.apply(this,arguments)}}}}}},0,0,0,0,0,0,[Ext,"Mixin"],0));Ext.util=Ext.util||{};Ext.util.DelayedTask=function(e,d,b,i,h){var g=this,a,c=function(){var j=Ext.GlobalEvents;clearInterval(g.id);g.id=null;e.apply(d,b||[]);if(h!==false&&j.hasListeners.idle){j.fireEvent("idle")}};i=typeof i==="boolean"?i:true;g.id=null;g.delay=function(k,m,l,j){if(i){g.cancel()}if(typeof k==="number"){a=k}e=m||e;d=l||d;b=j||b;if(!g.id){g.id=Ext.interval(c,a)}};g.cancel=function(){if(g.id){clearInterval(g.id);g.id=null}}};(Ext.cmd.derive("Ext.util.Event",Ext.Base,function(){var d=Array.prototype.slice,a=Ext.Array.insert,c=Ext.Array.toArray,b={};return{isEvent:true,suspended:0,noOptions:{},constructor:function(g,e){this.name=e;this.observable=g;this.listeners=[]},addListener:function(n,e,g,u,q){var x=this,l=false,p=x.observable,k=x.name,r,j,w,t,y,s,h,m,v,o;if(x.findListener(n,e)===-1){j=x.createListener(n,e,g,u,q);if(x.firing){x.listeners=x.listeners.slice(0)}r=x.listeners;m=h=r.length;w=g&&g.priority;y=x._highestNegativePriorityIndex;s=y!==undefined;if(w){t=(w<0);if(!t||s){for(v=(t?y:0);v0},fireDelegated:function(g,e){this.firingObservable=g;return this.fire.apply(this,e)},fire:function(){var B=this,s=B.listeners,m=s.length,p=B.observable,t=p.isElement,A=p.isComponent,y=B.firingObservable,h,x,q,v,g,k,w,u,C,j,o,l,z,r,n;if(!B.suspended&&m>0){B.firing=true;g=arguments.length?d.call(arguments,0):[];w=g.length;if(t){z=g[0]}for(v=0;v4?q:k;q=k;for(k in q){if(q.hasOwnProperty(k)){i=q[k];if(!o.$eventOptions[k]){l.addManagedListener(o,k,i.fn||i,i.scope||q.scope||p,i.fn?i:h,true)}}}if(q&&q.destroyable){return new b(l,o,q)}}else{if(m!==d){o.doAddListener(k,m,p,q,null,l,l);if(!j&&q&&q.destroyable){return new b(l,o,k,m,p)}}}},removeManagedListener:function(p,k,n,q){var m=this,r,j,o,h,l;if(typeof k!=="string"){r=k;for(k in r){if(r.hasOwnProperty(k)){j=r[k];if(!p.$eventOptions[k]){m.removeManagedListener(p,k,j.fn||j,j.scope||r.scope||q)}}}}else{o=m.managedListeners?m.managedListeners.slice():[];k=Ext.canonicalEventName(k);for(l=0,h=o.length;l0,h=this.events;if(!i&&j&&h){j=h[j];if(j&&j.isEvent){return j.isSuspended()}}return i},suspendEvents:function(h){++this.eventsSuspended;if(h&&!this.eventQueue){this.eventQueue=[]}},suspendEvent:function(){var n=this,l=n.events,h=arguments.length,k,m,j;for(k=0;k0){o.push(i)}l--;if(l===0){r.resolve(o)}return i};p=function(i){g--;if(g===0){r.reject(new Error("Too few Promises were resolved."))}return i};for(n=k=0,m=j.length;k0){c.type=d.substring(0,b);c.defaultType=d.substring(b+1)}if(h){delete e.factoryConfig;Ext.apply(c,h)}a=Ext.Factory.define(c.type,c);if(g.create===Ext.Base.create){g.create=a}}},0,0,0,0,0,0,[Ext.mixin,"Factoryable"],0));(Ext.cmd.derive("Ext.data.request.Base",Ext.Base,{factoryConfig:{type:"request",defaultType:"ajax"},result:null,success:null,timer:null,constructor:function(a){var b=this;Ext.apply(b,a.options||{},a.ownerConfig);b.id=++Ext.data.Connection.requestId;b.owner=a.owner;b.options=a.options;b.requestOptions=a.requestOptions},start:function(){var a=this,b=a.getTimeout();if(b&&a.async){a.timer=Ext.defer(a.onTimeout,b,a)}},abort:function(){var a=this;a.clearTimer();if(!a.timedout){a.aborted=true}a.abort=Ext.emptyFn},createDeferred:function(){return(this.deferred=new Ext.Deferred())},getDeferred:function(){return this.deferred||this.createDeferred()},getPromise:function(){return this.getDeferred().promise},then:function(){var a=this.getPromise();return a.then.apply(a,arguments)},onComplete:function(){var c=this,b=c.deferred,a=c.result;c.clearTimer();if(b){if(c.success){b.resolve(a)}else{b.reject(a)}}},onTimeout:function(){var a=this;a.timedout=true;a.timer=null;a.abort(true)},getTimeout:function(){return this.timeout},clearTimer:function(){var a=this.timer;if(a){clearTimeout(a);this.timer=null}},destroy:function(){var a=this;a.abort();a.owner=a.options=a.requestOptions=a.result=null;a.callParent()},privates:{createException:function(){var b=this,a;a={request:b,requestId:b.id,status:b.aborted?-1:0,statusText:b.aborted?"transaction aborted":"communication failure",getResponseHeader:b._getHeader,getAllResponseHeaders:b._getHeaders};if(b.aborted){a.aborted=true}if(b.timedout){a.timedout=true}return a},_getHeader:function(a){var b=this.headers;return b&&b[a.toLowerCase()]},_getHeaders:function(){return this.headers}}},1,0,0,0,0,[[Ext.mixin.Factoryable.prototype.mixinId||Ext.mixin.Factoryable.$className,Ext.mixin.Factoryable]],[Ext.data.request,"Base"],0));(Ext.cmd.derive("Ext.data.flash.BinaryXhr",Ext.Base,{statics:{flashPluginActivated:function(){Ext.data.flash.BinaryXhr.flashPluginActive=true;Ext.data.flash.BinaryXhr.flashPlugin=document.getElementById("ext-flash-polyfill");Ext.GlobalEvents.fireEvent("flashready")},flashPluginActive:false,flashPluginInjected:false,connectionIndex:1,liveConnections:{},flashPlugin:null,onFlashStateChange:function(d,c,b){var a;a=this.liveConnections[Number(d)];if(a){a.onFlashStateChange(c,b)}},registerConnection:function(b){var a=this.connectionIndex;this.conectionIndex=this.connectionIndex+1;this.liveConnections[a]=b;return a},injectFlashPlugin:function(){var b=this,a,c;b.flashPolyfillEl=Ext.getBody().appendChild({id:"ext-flash-polyfill",cn:[{tag:"p",html:"To view this page ensure that Adobe Flash Player version 11.1.0 or greater is installed."},{tag:"a",href:"http://www.adobe.com/go/getflashplayer",cn:[{tag:"img",src:window.location.protocol+"//www.adobe.com/images/shared/download_buttons/get_flash_player.gif",alt:"Get Adobe Flash player"}]}]});a=[Ext.Loader.getPath("Ext.data.Connection"),"../../../plugins/flash/swfobject.js"].join("/");c="/plugins/flash/FlashPlugin.swf";if(Ext.flashPluginPath){c=Ext.flashPluginPath}Ext.Loader.loadScript({url:a,onLoad:function(){var e="11.4.0";var h="playerProductInstall.swf";var d={};var i={};i.quality="high";i.bgcolor="#ffffff";i.allowscriptaccess="sameDomain";i.allowfullscreen="true";var g={};g.id="ext-flash-polyfill";g.name="polyfill";g.align="middle";swfobject.embedSWF(c,"ext-flash-polyfill","0","0",e,h,d,i,g)},onError:function(){},scope:b});Ext.data.flash.BinaryXhr.flashPluginInjected=true}},readyState:0,status:0,statusText:"",responseBytes:null,javascriptId:null,constructor:function(a){if(!Ext.data.flash.BinaryXhr.flashPluginInjected){Ext.data.flash.BinaryXhr.injectFlashPlugin()}var b=this;Ext.apply(b,a);b.requestHeaders={}},abort:function(){var a=this;if(a.readyState==4){return}a.aborted=true;if(!Ext.data.flash.BinaryXhr.flashPluginActive){Ext.GlobalEvents.removeListener("flashready",a.onFlashReady,a);return}Ext.data.flash.BinaryXhr.flashPlugin.abortRequest(a.javascriptId);delete Ext.data.flash.BinaryXhr.liveConnections[a.javascriptId]},getAllResponseHeaders:function(){var a=[];Ext.Object.each(this.responseHeaders,function(b,c){a.push(b+": "+c)});return a.join("\r\n")},getResponseHeader:function(b){var a=this.responseHeaders;return(a&&a[b])||null},open:function(g,c,d,a,b){var e=this;e.method=g;e.url=c;e.async=d!==false;e.user=a;e.password=b},overrideMimeType:function(a){this.mimeType=a},send:function(a){var b=this;b.body=a;if(!Ext.data.flash.BinaryXhr.flashPluginActive){Ext.GlobalEvents.addListener("flashready",b.onFlashReady,b)}else{this.onFlashReady()}},onFlashReady:function(){var c=this,b,a;c.javascriptId=Ext.data.flash.BinaryXhr.registerConnection(c);b={method:c.method,url:c.url,user:c.user,password:c.password,mimeType:c.mimeType,requestHeaders:c.requestHeaders,body:c.body,javascriptId:c.javascriptId};a=Ext.data.flash.BinaryXhr.flashPlugin.postBinary(b)},setReadyState:function(b){var a=this;if(a.readyState!=b){a.readyState=b;a.onreadystatechange()}},setRequestHeader:function(b,a){this.requestHeaders[b]=a},onreadystatechange:Ext.emptyFn,parseData:function(b){var a=this;this.status=b.status||0;a.responseHeaders={};if(a.mimeType){a.responseHeaders["content-type"]=a.mimeType}if(b.reason=="complete"){this.responseBytes=b.data;a.responseHeaders["content-length"]=b.data.length}else{if(b.reason=="error"||b.reason=="securityError"){this.statusText=b.text;a.responseHeaders["content-length"]=0}}},onFlashStateChange:function(c,b){var a=this;if(c==4){a.parseData(b);delete Ext.data.flash.BinaryXhr.liveConnections[a.javascriptId]}a.setReadyState(c)}},1,0,0,0,0,0,[Ext.data.flash,"BinaryXhr"],0));(Ext.cmd.derive("Ext.data.request.Ajax",Ext.data.request.Base,{statics:{parseStatus:function(a){a=a==1223?204:a;var c=(a>=200&&a<300)||a==304,b=false;if(!c){switch(a){case 12002:case 12029:case 12030:case 12031:case 12152:case 13030:b=true;break}}return{success:c,isException:b}}},start:function(e){var c=this,b=c.options,a=c.requestOptions,d=c.isXdr,h,g;h=c.xhr=c.openRequest(b,a,c.async,c.username,c.password);if(!d){g=c.setupHeaders(h,b,a.data,a.params)}if(c.async){if(!d){h.onreadystatechange=Ext.Function.bind(c.onStateChange,c)}}if(d){c.processXdrRequest(c,h)}Ext.data.request.Base.prototype.start.call(this,e);h.send(e);if(!c.async){return c.onComplete()}return c},abort:function(b){var a=this,d=a.xhr;if(b||a.isLoading()){try{d.onreadystatechange=null}catch(c){d.onreadystatechange=Ext.emptyFn}d.abort();Ext.data.request.Base.prototype.abort.call(this,b);a.onComplete();a.cleanup()}},cleanup:function(){this.xhr=null;delete this.xhr},isLoading:function(){var a=this,d=a.xhr,b=d&&d.readyState,c=Ext.data.flash&&Ext.data.flash.BinaryXhr;if(!d||a.aborted||a.timedout){return false}if(c&&d instanceof c){return b!==4}return b!==0&&b!==4},openRequest:function(c,a,d,h,b){var e=this,g=e.newRequest(c);if(h){g.open(a.method,a.url,d,h,b)}else{if(e.isXdr){g.open(a.method,a.url)}else{g.open(a.method,a.url,d)}}if(c.binary||e.binary){if(window.Uint8Array){g.responseType="arraybuffer"}else{if(g.overrideMimeType){g.overrideMimeType("text/plain; charset=x-user-defined")}}}if(c.withCredentials||e.withCredentials){g.withCredentials=true}return g},newRequest:function(a){var b=this,c;if(a.binaryData){if(window.Uint8Array){c=b.getXhrInstance()}else{c=new Ext.data.flash.BinaryXhr()}}else{if(b.cors&&Ext.isIE9m){c=b.getXdrInstance();b.isXdr=true}else{c=b.getXhrInstance();b.isXdr=false}}return c},setupHeaders:function(o,p,g,d){var k=this,b=Ext.apply({},p.headers||{},k.defaultHeaders),n=k.defaultPostHeader,l=p.jsonData,a=p.xmlData,j="Content-Type",c=k.useDefaultXhrHeader,m,h;if(!b.hasOwnProperty(j)&&(g||d)){if(g){if(p.rawData){n="text/plain"}else{if(a&&Ext.isDefined(a)){n="text/xml"}else{if(l&&Ext.isDefined(l)){n="application/json"}}}}b[j]=n}if(c&&!b["X-Requested-With"]){b["X-Requested-With"]=k.defaultXhrHeader}if(b[j]===undefined||b[j]===null){delete b[j]}try{for(m in b){if(b.hasOwnProperty(m)){h=b[m];o.setRequestHeader(m,h)}}}catch(i){k.owner.fireEvent("exception",m,h)}return b},getXdrInstance:function(){var a;if(Ext.ieVersion>=8){a=new XDomainRequest()}else{Ext.raise({msg:"Your browser does not support CORS"})}return a},getXhrInstance:(function(){var b=[function(){return new XMLHttpRequest()},function(){return new ActiveXObject("MSXML2.XMLHTTP.3.0")},function(){return new ActiveXObject("MSXML2.XMLHTTP")},function(){return new ActiveXObject("Microsoft.XMLHTTP")}],c=0,a=b.length,g;for(;c=0){i=l.substr(0,g).toLowerCase();if(l.charAt(g+1)==" "){++g}b[i]=l.substr(g+1)}}d={request:h,requestId:h.id,status:j.status,statusText:j.statusText,getResponseHeader:function(m){return b[m.toLowerCase()]},getAllResponseHeaders:function(){return b}};if(c){h.processXdrResponse(d,j)}if(h.binary){d.responseBytes=h.getByteArray(j)}else{d.responseText=j.responseText;d.responseXML=j.responseXML}return d},destroy:function(){this.xhr=null;Ext.data.request.Base.prototype.destroy.call(this)},privates:{getByteArray:function(k){var c=k.response,b=k.responseBody,l=Ext.data.flash&&Ext.data.flash.BinaryXhr,a,j,g,d;if(k instanceof l){a=k.responseBytes}else{if(window.Uint8Array){a=c?new Uint8Array(c):[]}else{if(Ext.isIE9p){try{a=new VBArray(b).toArray()}catch(h){a=[]}}else{if(Ext.isIE){if(!this.self.vbScriptInjected){this.injectVBScript()}getIEByteArray(k.responseBody,a=[])}else{a=[];j=k.responseText;g=j.length;for(d=0;d=500){this.run()}},run:function(){var e=this;if(!e.isRunning){return}var a=e.runningQueue,b=Ext.now(),c,d;e.lastRunTime=b;e.frameStartTime=b;a.push.apply(a,e.queue);for(c=0,d=a.length;c0){b=a.shift();this.invoke(b);this.processIdleQueue()}},processTaskQueue:function(){if(!this.hasOwnProperty("taskQueueTimer")){this.taskQueueTimer=Ext.defer(this.processTaskQueueItem,15,this)}},processTaskQueueItem:function(){delete this.taskQueueTimer;var a=this.taskQueue,b;if(a.length>0){b=a.shift();this.invoke(b);this.processTaskQueue()}}},1,0,0,0,0,0,[Ext,"AnimationQueue"],0));(Ext.cmd.derive("Ext.ComponentManager",Ext.Base,{alternateClassName:"Ext.ComponentMgr",singleton:true,count:0,typeName:"xtype",constructor:function(a){var b=this;Ext.apply(b,a||{});b.all={};b.references={};b.onAvailableCallbacks={}},create:function(a,b){if(typeof a==="string"){return Ext.widget(a)}if(a.isComponent){return a}if("xclass" in a){return Ext.create(a.xclass,a)}return Ext.widget(a.xtype||b,a)},get:function(a){return this.all[a]},register:function(a){var e=this,d=e.all,c=a.getId(),b=e.onAvailableCallbacks;d[c]=a;if(a.getReference&&a.getReference()){e.references[c]=a}++e.count;if(!e.hasFocusListener){Ext.on("focus",e.onGlobalFocus,e);e.hasFocusListener=true}b=b&&b[c];if(b&&b.length){e.notifyAvailable(a)}},unregister:function(a){var b=a.getId();if(a.getReference&&a.getReference()){this.references[b]=null;delete this.references[b]}this.all[b]=null;delete this.all[b];this.count--},markReferencesDirty:function(){this.referencesDirty=true},fixReferences:function(){var c=this,b=c.references,a;if(c.referencesDirty){for(a in b){if(b.hasOwnProperty(a)){b[a].fixReference()}}c.referencesDirty=false}},onAvailable:function(h,c,b){var g=this,e=g.onAvailableCallbacks,a=g.all,d;if(h in a){d=a[h];c.call(b||d,d)}else{if(h){if(!Ext.isArray(e[h])){e[h]=[]}e[h].push(function(i){c.call(b||i,i)})}}},notifyAvailable:function(b){var a=this.onAvailableCallbacks[b&&b.getId()]||[];while(a.length){(a.shift())(b)}},each:function(b,a){return Ext.Object.each(this.all,b,a)},getCount:function(){return this.count},getAll:function(){return Ext.Object.getValues(this.all)},getActiveComponent:function(){return Ext.Component.fromElement(Ext.dom.Element.getActiveElement())},onGlobalFocus:function(i){var d=this,b=i.toElement,j=i.fromElement,h=Ext.Component.fromElement(b),a=Ext.Component.fromElement(j),c,g;if(h===a){return}c=d.getCommonAncestor(a,h);if(a&&!(a.destroyed||a.destroying)){if(a.handleBlurEvent){a.handleBlurEvent(i)}for(g=a;g&&g!==c;g=g.getRefOwner()){if(!(g.destroyed||g.destroying)){g.onFocusLeave({event:i.event,type:"focusleave",target:j,relatedTarget:b,fromComponent:a,toComponent:h})}}}if(h&&!h.destroyed){if(h.handleFocusEvent){h.handleFocusEvent(i)}for(g=h;g&&g!==c;g=g.getRefOwner()){g.onFocusEnter({event:i.event,type:"focusenter",relatedTarget:j,target:b,fromComponent:a,toComponent:h})}}},getCommonAncestor:function(b,a){if(b===a){return b}while(b&&!(b.isAncestor(a)||b===a)){b=b.getRefOwner()}return b},privates:{clearAll:function(){this.all={};this.references={};this.onAvailableCallbacks={}},fromElement:function(b,d,c){var h=Ext.getDom(b),a=this.all,e=0,j,g,i;if(typeof d!=="number"){j=Ext.getDom(d);d=Number.MAX_VALUE}while(h&&h.nodeType===1&&e0){for(;b.first&&c;c--){b.removeAtKey(b.first.key)}}},destroy:function(){this.first=this.last=null;Ext.util.HashMap.prototype.destroy.call(this)}},0,0,0,0,0,0,[Ext.util,"LruCache"],0));(Ext.cmd.derive("Ext.ComponentQuery",Ext.Base,{singleton:true},0,0,0,0,0,0,[Ext,"ComponentQuery"],function(){var g=this,s=Ext.util.Operators,k=/(\d*)n\+?(\d*)/,e=/\D/,m=/^(\s)+/,l=/\\(.)/g,n=new Ext.util.LruCache({maxSize:100}),o=["var r = [],","i = 0,","it = items,","l = it.length,","c;","for (; i < l; i++) {","c = it[i];","if (c.{0}) {","r.push(c);","}","}","return r;"].join(""),p=function(u,t){return t.method.apply(this,[u].concat(t.args))},a=function(v,z){var t=[],w=0,y=v.length,x,u=z!==">";for(;w\^])\s?|\s|$)/,q=/^(#)?((?:\\\.|[\w\-])+|\*)(?:\((true|false)\))?/,c=[{re:/^\.((?:\\\.|[\w\-])+)(?:\((true|false)\))?/,method:d,argTransform:function(t){if(t[1]!==undefined){t[1]=t[1].replace(l,"$1")}return t.slice(1)}},{re:/^(?:\[((?:[@?$])?[\w\-]*)\s*(?:([\^$*~%!\/]?=)\s*(['"])?((?:\\\]|.)*?)\3)?(?!\\)\])/,method:b,argTransform:function(w){var t=w[0],x=w[1],u=w[2],v=w[4],y;if(v!==undefined){v=v.replace(l,"$1")}if(u==="/="){y=n.get(v);if(y){v=y}else{v=n.add(v,new RegExp(v))}}return[x,u,v]}},{re:/^#((?:\\\.|[\w\-])+)/,method:i},{re:/^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/,method:r,argTransform:function(t){if(t[2]!==undefined){t[2]=t[2].replace(l,"$1")}return t.slice(1)}},{re:/^(?:\{([^\}]+)\})/,method:o}];g.Query=Ext.extend(Object,{constructor:function(t){t=t||{};Ext.apply(this,t)},execute:function(v){var w=this.operations,u=[],y,x,t;for(x=0,t=w.length;x=0;--x){v=t[x];y=v.mode;if(y){if(y==="^"){u=a(u," ")}else{if(y===">"){A=[];for(w=0,z=u.length;w1}});Ext.apply(g,{cache:new Ext.util.LruCache({maxSize:100}),pseudos:{not:function(z,t){var x=0,y=z.length,w=[],v=-1,u;for(;x0){t.push(u[0])}return t},last:function(v){var t=v.length,u=[];if(t>0){u.push(v[t-1])}return u},focusable:function(u){var t=u.length,w=[],v=0,x;for(;v=t.left)&&((b=="t"&&i=="b")||(b=="b"&&i=="t"));q=(k=t.top)&&((c=="r"&&l=="l")||(c=="l"&&l=="r"));if(m+g>v.right){if(q){m=t.left-g;q=false}else{m=v.right-g}}if(mv.bottom){if(p){k=t.top-s;p=false}else{k=v.bottom-s}}if(kh.right){j=true;d[0]=(h.right-a.right)}if(a.left+d[0]h.bottom){j=true;d[1]=(h.bottom-a.bottom)}if(a.top+d[1]=a.x&&b.right<=a.right&&b.y>=a.y&&b.bottom<=a.bottom)},intersect:function(h){var g=this,d=Math.max(g.y,h.y),e=Math.min(g.right,h.right),a=Math.min(g.bottom,h.bottom),c=Math.max(g.x,h.x);if(a>d&&e>c){return new this.self(d,e,a,c)}else{return false}},union:function(h){var g=this,d=Math.min(g.y,h.y),e=Math.max(g.right,h.right),a=Math.max(g.bottom,h.bottom),c=Math.min(g.x,h.x);return new this.self(d,e,a,c)},constrainTo:function(b){var a=this,c=Ext.Number.constrain;a.top=a.y=c(a.top,b.y,b.bottom);a.bottom=c(a.bottom,b.y,b.bottom);a.left=a.x=c(a.left,b.x,b.right);a.right=c(a.right,b.x,b.right);return a},adjust:function(e,b,a,d){var c=this;c.top=c.y+=e;c.left=c.x+=d;c.right+=b;c.bottom+=a;return c},getOutOfBoundOffset:function(a,b){if(!Ext.isObject(a)){if(a=="x"){return this.getOutOfBoundOffsetX(b)}else{return this.getOutOfBoundOffsetY(b)}}else{b=a;var c=new Ext.util.Offset();c.x=this.getOutOfBoundOffsetX(b.x);c.y=this.getOutOfBoundOffsetY(b.y);return c}},getOutOfBoundOffsetX:function(a){if(a<=this.x){return this.x-a}else{if(a>=this.right){return this.right-a}}return 0},getOutOfBoundOffsetY:function(a){if(a<=this.y){return this.y-a}else{if(a>=this.bottom){return this.bottom-a}}return 0},isOutOfBound:function(a,b){if(!Ext.isObject(a)){if(a=="x"){return this.isOutOfBoundX(b)}else{return this.isOutOfBoundY(b)}}else{b=a;return(this.isOutOfBoundX(b.x)||this.isOutOfBoundY(b.y))}},isOutOfBoundX:function(a){return(athis.right)},isOutOfBoundY:function(a){return(athis.bottom)},restrict:function(b,d,a){if(Ext.isObject(b)){var c;a=d;d=b;if(d.copy){c=d.copy()}else{c={x:d.x,y:d.y}}c.x=this.restrictX(d.x,a);c.y=this.restrictY(d.y,a);return c}else{if(b=="x"){return this.restrictX(d,a)}else{return this.restrictY(d,a)}}},restrictX:function(b,a){if(!a){a=1}if(b<=this.x){b-=(b-this.x)*a}else{if(b>=this.right){b-=(b-this.right)*a}}return b},restrictY:function(b,a){if(!a){a=1}if(b<=this.y){b-=(b-this.y)*a}else{if(b>=this.bottom){b-=(b-this.bottom)*a}}return b},getSize:function(){return{width:this.right-this.x,height:this.bottom-this.y}},copy:function(){return new this.self(this.y,this.right,this.bottom,this.x)},copyFrom:function(b){var a=this;a.top=a.y=a[1]=b.y;a.right=b.right;a.bottom=b.bottom;a.left=a.x=a[0]=b.x;return this},toString:function(){return"Region["+this.top+","+this.right+","+this.bottom+","+this.left+"]"},translateBy:function(a,c){if(arguments.length==1){c=a.y;a=a.x}var b=this;b.top=b.y+=c;b.right+=a;b.bottom+=c;b.left=b.x+=a;return b},round:function(){var a=this;a.top=a.y=Math.round(a.y);a.right=Math.round(a.right);a.bottom=Math.round(a.bottom);a.left=a.x=Math.round(a.x);return a},equals:function(a){return(this.top===a.top&&this.right===a.right&&this.bottom===a.bottom&&this.left===a.left)}},3,0,0,0,0,0,[Ext.util,"Region"],0));(Ext.cmd.derive("Ext.util.Point",Ext.util.Region,{radianToDegreeConstant:180/Math.PI,origin:{x:0,y:0},statics:{fromEvent:function(b){var a=b.changedTouches,c=(a&&a.length>0)?a[0]:b;return this.fromTouch(c)},fromTouch:function(a){return new this(a.pageX,a.pageY)},from:function(a){if(!a){return new this(0,0)}if(!(a instanceof this)){return new this(a.x,a.y)}return a}},constructor:function(a,b){if(a==null){a=0}if(b==null){b=0}Ext.util.Region.prototype.constructor.call(this,b,a,b,a)},clone:function(){return new this.self(this.x,this.y)},copy:function(){return this.clone.apply(this,arguments)},copyFrom:function(a){this.x=a.x;this.y=a.y;return this},toString:function(){return"Point["+this.x+","+this.y+"]"},equals:function(a){return(this.x===a.x&&this.y===a.y)},isCloseTo:function(c,b){if(typeof b=="number"){return this.getDistanceTo(c)<=b}var a=c.x,g=c.y,e=b.x,d=b.y;return(this.x<=a+e&&this.x>=a-e&&this.y<=g+d&&this.y>=g-d)},isWithin:function(){return this.isCloseTo.apply(this,arguments)},isContainedBy:function(a){if(!(a instanceof Ext.util.Region)){a=Ext.get(a.el||a).getRegion()}return a.contains(this)},roundedEquals:function(a){if(!a||typeof a!=="object"){a=this.origin}return(Math.round(this.x)===Math.round(a.x)&&Math.round(this.y)===Math.round(a.y))},getDistanceTo:function(b){if(!b||typeof b!=="object"){b=this.origin}var c=this.x-b.x,a=this.y-b.y;return Math.sqrt(c*c+a*a)},getAngleTo:function(b){if(!b||typeof b!=="object"){b=this.origin}var c=this.x-b.x,a=this.y-b.y;return Math.atan2(a,c)*this.radianToDegreeConstant}},3,0,0,0,0,0,[Ext.util,"Point"],function(){this.prototype.translate=this.prototype.translateBy}));(Ext.cmd.derive("Ext.event.Event",Ext.Base,{alternateClassName:"Ext.EventObjectImpl",isStopped:false,defaultPrevented:false,isEvent:true,statics:{resolveTextNode:function(a){return(a&&a.nodeType===3)?a.parentNode:a},pointerEvents:{pointerdown:1,pointermove:1,pointerup:1,pointercancel:1,pointerover:1,pointerout:1,pointerenter:1,pointerleave:1,MSPointerDown:1,MSPointerMove:1,MSPointerUp:1,MSPointerOver:1,MSPointerOut:1,MSPointerCancel:1,MSPointerEnter:1,MSPointerLeave:1},mouseEvents:{mousedown:1,mousemove:1,mouseup:1,mouseover:1,mouseout:1,mouseenter:1,mouseleave:1},clickEvents:{click:1,dblclick:1},touchEvents:{touchstart:1,touchmove:1,touchend:1,touchcancel:1},focusEvents:{focus:1,blur:1,focusin:1,focusout:1,focusenter:1,focusleave:1},pointerTypes:{2:"touch",3:"pen",4:"mouse",touch:"touch",pen:"pen",mouse:"mouse"}},constructor:function(b){var g=this,j=g.self,e=g.self.resolveTextNode,i=b.changedTouches,c=i?i[0]:b,h=b.type,a,d;g.pageX=c.pageX;g.pageY=c.pageY;g.target=g.delegatedTarget=e(b.target);d=b.relatedTarget;if(d){g.relatedTarget=e(d)}g.browserEvent=g.event=b;g.type=h;g.button=b.button||0;g.shiftKey=b.shiftKey;g.ctrlKey=b.ctrlKey||b.metaKey||false;g.altKey=b.altKey;g.charCode=b.charCode;g.keyCode=b.keyCode;g.buttons=b.buttons;if(g.button===0&&g.buttons===0){g.buttons=1}if(j.forwardTab!==undefined&&j.focusEvents[h]){g.forwardTab=j.forwardTab}if(j.mouseEvents[h]||j.clickEvents[h]){a="mouse"}else{if(j.pointerEvents[h]){a=j.pointerTypes[b.pointerType]}else{if(j.touchEvents[h]){a="touch"}}}if(a){g.pointerType=a}g.timeStamp=g.time=+(b.timeStamp||new Date())},chain:function(a){var b=Ext.Object.chain(this);b.parentEvent=this;return Ext.apply(b,a)},correctWheelDelta:function(c){var b=this.WHEEL_SCALE,a=Math.round(c/b);if(!a&&c){a=(c<0)?-1:1}return a},getCharCode:function(){return this.charCode||this.keyCode},getKey:function(){return this.keyCode||this.charCode},getKeyName:function(){return this.keyCodes[this.keyCode]},getPoint:function(){var a=this.getXY();return new Ext.util.Point(a[0],a[1])},getRelatedTarget:function(b,e,a){var c=this.relatedTarget,d=null;if(c){if(b){d=Ext.fly(c).findParent(b,e,a)}else{d=a?Ext.get(c):c}}return d},getTarget:function(b,c,a){return b?Ext.fly(this.target).findParent(b,c,a):(a?Ext.get(this.target):this.target)},getTime:function(){return this.time},getWheelDelta:function(){var a=this.getWheelDeltas();return a.y},getWheelDeltas:function(){var d=this,c=d.browserEvent,b=0,a=0;if(Ext.isDefined(c.wheelDeltaX)){b=c.wheelDeltaX;a=c.wheelDeltaY}else{if(c.wheelDelta){a=c.wheelDelta}else{if(c.detail){a=-c.detail;if(a>100){a=3}else{if(a<-100){a=-3}}if(Ext.isDefined(c.axis)&&c.axis===c.HORIZONTAL_AXIS){b=a;a=0}}}}return{x:d.correctWheelDelta(b),y:d.correctWheelDelta(a)}},getX:function(){return this.getXY()[0]},getXY:function(){var c=this,e=c.xy;if(!e){e=c.xy=[c.pageX,c.pageY];var b=e[0],h,d,g,a;if(!b&&b!==0){h=c.browserEvent;d=document;g=d.documentElement;a=d.body;e[0]=h.clientX+(g&&g.scrollLeft||a&&a.scrollLeft||0)-(g&&g.clientLeft||a&&a.clientLeft||0);e[1]=h.clientY+(g&&g.scrollTop||a&&a.scrollTop||0)-(g&&g.clientTop||a&&a.clientTop||0)}}return e},getY:function(){return this.getXY()[1]},hasModifier:function(){var a=this;return !!(a.ctrlKey||a.altKey||a.shiftKey||a.metaKey)},isNavKeyPress:function(d){var c=this,a=c.keyCode,b=c.type==="keypress";return((!b||Ext.isGecko)&&a>=33&&a<=40)||(!d&&(a===c.RETURN||a===c.TAB||a===c.ESC))},isSpecialKey:function(){var d=this,b=d.keyCode,a=Ext.isGecko,c=d.type==="keypress";return(a&&c&&d.charCode===0)||(this.isNavKeyPress())||(b===d.BACKSPACE)||(b===d.ENTER)||(b>=16&&b<=20)||((!c||a)&&b>=44&&b<=46)},makeUnpreventable:function(){this.browserEvent.preventDefault=Ext.emptyFn},preventDefault:function(){var b=this,a=b.parentEvent;b.defaultPrevented=true;if(a){a.defaultPrevented=true}b.browserEvent.preventDefault();return b},setCurrentTarget:function(a){this.currentTarget=this.delegatedTarget=a},stopEvent:function(){return this.preventDefault().stopPropagation()},stopPropagation:function(){var b=this,c=b.browserEvent,a=b.parentEvent;b.isStopped=true;if(a){a.isStopped=true}if(!c.stopPropagation){c.cancelBubble=true;return b}c.stopPropagation();return b},within:function(c,d,a){var b;if(c){b=d?this.getRelatedTarget():this.getTarget()}return b?Ext.fly(c).contains(b)||!!(a&&b===Ext.getDom(c)):false},deprecated:{"4.0":{methods:{getPageX:"getX",getPageY:"getY"}}}},1,0,0,0,0,0,[Ext.event,"Event",Ext,"EventObjectImpl"],function(a){var c=a.prototype,d={BACKSPACE:8,TAB:9,NUM_CENTER:12,ENTER:13,RETURN:13,SHIFT:16,CTRL:17,ALT:18,PAUSE:19,CAPS_LOCK:20,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,PRINT_SCREEN:44,INSERT:45,DELETE:46,ZERO:48,ONE:49,TWO:50,THREE:51,FOUR:52,FIVE:53,SIX:54,SEVEN:55,EIGHT:56,NINE:57,A:65,B:66,C:67,D:68,E:69,F:70,G:71,H:72,I:73,J:74,K:75,L:76,M:77,N:78,O:79,P:80,Q:81,R:82,S:83,T:84,U:85,V:86,W:87,X:88,Y:89,Z:90,CONTEXT_MENU:93,NUM_ZERO:96,NUM_ONE:97,NUM_TWO:98,NUM_THREE:99,NUM_FOUR:100,NUM_FIVE:101,NUM_SIX:102,NUM_SEVEN:103,NUM_EIGHT:104,NUM_NINE:105,NUM_MULTIPLY:106,NUM_PLUS:107,NUM_MINUS:109,NUM_PERIOD:110,NUM_DIVISION:111,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,WHEEL_SCALE:(function(){var h;if(Ext.isGecko){h=3}else{if(Ext.isMac){if(Ext.isSafari&&Ext.webKitVersion>=532){h=120}else{h=12}h*=3}else{h=120}}return h}())},b={},e,g;Ext.apply(a,d);Ext.apply(c,d);delete d.WHEEL_SCALE;delete d.RETURN;for(e in d){g=d[e];b[g]=e}c.keyCodes=b;c.getTrueXY=c.getXY}));Ext.define("Ext.overrides.event.Event",{override:"Ext.event.Event",mousedownEvents:{mousedown:1,pointerdown:1,touchstart:1},injectEvent:(function(){var d,e={},c;if(!Ext.isIE9m&&document.createEvent){d={createHtmlEvent:function(k,i,h,g){var j=k.createEvent("HTMLEvents");j.initEvent(i,h,g);return j},createMouseEvent:function(u,s,m,l,o,k,i,j,g,r,q,n,p){var h=u.createEvent("MouseEvents"),t=u.defaultView||window;if(h.initMouseEvent){h.initMouseEvent(s,m,l,t,o,k,i,k,i,j,g,r,q,n,p)}else{h=u.createEvent("UIEvents");h.initEvent(s,m,l);h.view=t;h.detail=o;h.screenX=k;h.screenY=i;h.clientX=k;h.clientY=i;h.ctrlKey=j;h.altKey=g;h.metaKey=q;h.shiftKey=r;h.button=n;h.relatedTarget=p}return h},createUIEvent:function(m,k,i,h,j){var l=m.createEvent("UIEvents"),g=m.defaultView||window;l.initUIEvent(k,i,h,g,j);return l},fireEvent:function(i,g,h){i.dispatchEvent(h)}}}else{if(document.createEventObject){c={0:1,1:4,2:2};d={createHtmlEvent:function(k,i,h,g){var j=k.createEventObject();j.bubbles=h;j.cancelable=g;return j},createMouseEvent:function(t,s,m,l,o,k,i,j,g,r,q,n,p){var h=t.createEventObject();h.bubbles=m;h.cancelable=l;h.detail=o;h.screenX=k;h.screenY=i;h.clientX=k;h.clientY=i;h.ctrlKey=j;h.altKey=g;h.shiftKey=r;h.metaKey=q;h.button=c[n]||n;h.relatedTarget=p;return h},createUIEvent:function(l,j,h,g,i){var k=l.createEventObject();k.bubbles=h;k.cancelable=g;return k},fireEvent:function(i,g,h){i.fireEvent("on"+g,h)}}}}Ext.Object.each({load:[false,false],unload:[false,false],select:[true,false],change:[true,false],submit:[true,true],reset:[true,false],resize:[true,false],scroll:[true,false]},function(i,j){var h=j[0],g=j[1];e[i]=function(m,k){var l=d.createHtmlEvent(i,h,g);d.fireEvent(m,i,l)}});function b(i,h){var g=(i!=="mousemove");return function(m,j){var l=j.getXY(),k=d.createMouseEvent(m.ownerDocument,i,true,g,h,l[0],l[1],j.ctrlKey,j.altKey,j.shiftKey,j.metaKey,j.button,j.relatedTarget);d.fireEvent(m,i,k)}}Ext.each(["click","dblclick","mousedown","mouseup","mouseover","mousemove","mouseout"],function(g){e[g]=b(g,1)});Ext.Object.each({focusin:[true,false],focusout:[true,false],activate:[true,true],focus:[false,false],blur:[false,false]},function(i,j){var h=j[0],g=j[1];e[i]=function(m,k){var l=d.createUIEvent(m.ownerDocument,i,h,g,1);d.fireEvent(m,i,l)}});if(!d){e={};d={}}function a(h,g){}return function(j){var i=this,h=e[i.type]||a,g=j?(j.dom||j):i.getTarget();h(g,i)}}()),preventDefault:function(){var d=this,c=d.browserEvent,b=d.parentEvent,a,e;if(typeof c.type!=="unknown"){d.defaultPrevented=true;if(b){b.defaultPrevented=true}if(c.preventDefault){c.preventDefault()}else{if(c.type==="mousedown"){e=c.target;a=e.getAttribute("unselectable");if(a!=="on"){e.setAttribute("unselectable","on");Ext.defer(function(){e.setAttribute("unselectable",a)},1)}}c.returnValue=false;if(c.ctrlKey||c.keyCode>111&&c.keyCode<124){c.keyCode=-1}}}return d},stopPropagation:function(){var b=this,a=b.browserEvent;if(typeof a.type!=="unknown"){if(b.mousedownEvents[b.type]){Ext.GlobalEvents.fireMouseDown(b)}arguments.callee.$previous.call(this)}return b},deprecated:{"5.0":{methods:{clone:function(){return new this.self(this.browserEvent,this)}}}}},function(){var a=this,d,c=function(g){if(g.keyCode===9){a.forwardTab=!g.shiftKey}},b=function(g){if(g.keyCode===9){delete a.forwardTab}};if(Ext.isIE9m){d={0:0,1:0,4:1,2:2};a.override({statics:{enableIEAsync:function(h){var e,g={};for(e in h){g[e]=h[e]}return g}},constructor:function(i,j,g,e){var h=this;h.callParent([i,j,g,e]);h.button=d[i.button];if(i.type==="contextmenu"){h.button=2}h.toElement=i.toElement;h.fromElement=i.fromElement},mouseLeaveRe:/(mouseout|mouseleave)/,mouseEnterRe:/(mouseover|mouseenter)/,enableIEAsync:function(e){this.browserEvent=this.self.enableIEAsync(e)},getRelatedTarget:function(g,k,e){var i=this,h,j;if(!i.relatedTarget){h=i.type;if(i.mouseLeaveRe.test(h)){j=i.toElement}else{if(i.mouseEnterRe.test(h)){j=i.fromElement}}if(j){i.relatedTarget=i.self.resolveTextNode(j)}}return i.callParent([g,k,e])}});document.attachEvent("onkeydown",c);document.attachEvent("onkeyup",b);window.attachEvent("onunload",function(){document.detachEvent("onkeydown",c);document.detachEvent("onkeyup",b)})}else{if(document.addEventListener){document.addEventListener("keydown",c,true);document.addEventListener("keyup",b,true)}}});(Ext.cmd.derive("Ext.event.publisher.Dom",Ext.event.publisher.Publisher,{type:"dom",handledDomEvents:[],reEnterCount:0,captureEvents:{animationstart:1,animationend:1,resize:1,focus:1,blur:1,scroll:1},directEvents:{mouseenter:1,mouseleave:1,pointerenter:1,pointerleave:1,MSPointerEnter:1,MSPointerLeave:1,load:1,unload:1,beforeunload:1,error:1,DOMContentLoaded:1,DOMFrameContentLoaded:1,hashchange:1},blockedPointerEvents:{pointerover:1,pointerout:1,pointerenter:1,pointerleave:1,MSPointerOver:1,MSPointerOut:1,MSPointerEnter:1,MSPointerLeave:1},blockedCompatibilityMouseEvents:{mouseenter:1,mouseleave:1},constructor:function(){var a=this;a.bubbleSubscribers={};a.captureSubscribers={};a.directSubscribers={};a.directCaptureSubscribers={};a.delegatedListeners={};a.initHandlers();Ext.onInternalReady(a.onReady,a);Ext.event.publisher.Publisher.prototype.constructor.call(this)},registerEvents:function(){var g=this,d=Ext.event.publisher.Publisher.publishersByEvent,a=g.handledDomEvents,e=a.length,c=0,b;for(;cc)?1:(d1){m=[];for(c=0;c0){b.invokeRecognizers("onTouchMove",c)}}},onTouchEnd:function(b){var a=this;if(!a.isStarted){return}a.updateTouches(b,true);a.invokeRecognizers(a.isCancelEvent[b.type]?"onTouchCancel":"onTouchEnd",b);if(!a.activeTouches.length){a.isStarted=false;a.invokeRecognizers("onEnd",b);if(Ext.enableGarbageCollector){Ext.dom.GarbageCollector.resume()}}},onTargetTouchMove:function(a){if(Ext.elevateFunction){Ext.elevateFunction(this.doTargetTouchMove,this,[a])}else{this.doTargetTouchMove(a)}},doTargetTouchMove:function(a){if(!Ext.getBody().contains(a.target)){this.onTouchMove(new Ext.event.Event(a))}},onTargetTouchEnd:function(a){if(Ext.elevateFunction){Ext.elevateFunction(this.doTargetTouchEnd,this,[a])}else{this.doTargetTouchEnd(a)}},doTargetTouchEnd:function(c){var a=this,b=c.target;b.removeEventListener("touchmove",a.onTargetTouchMove);b.removeEventListener("touchend",a.onTargetTouchEnd);b.removeEventListener("touchcancel",a.onTargetTouchEnd);if(!Ext.getBody().contains(b)){a.onTouchEnd(new Ext.event.Event(c))}},updateAsync:function(a){this.handlers=a?this._asyncHandlers:this._handlers},reset:function(){var e=this,b=e.recognizers,d=b.length,c,a;e.activeTouchesMap={};e.activeTouches=[];e.changedTouches=[];e.isStarted=false;for(c=0;c=500){this.run()}},run:function(){this.pending=false;var k=this.readQueue,e=this.writeQueue,c=null,g;if(this.mode){g=k;if(e.length>0){c=false}}else{g=e;if(k.length>0){c=true}}var b=g.slice(),d,h,a,j,l;g.length=0;for(d=0,h=b.length;d2){j.apply(l,a[2])}else{j.call(l)}}b.length=0;if(c!==null){this.request(c)}}},1,0,0,0,0,0,[Ext,"TaskQueue"],0));(Ext.cmd.derive("Ext.util.sizemonitor.Abstract",Ext.Base,{config:{element:null,callback:Ext.emptyFn,scope:null,args:[]},width:0,height:0,contentWidth:0,contentHeight:0,constructor:function(a){this.refresh=Ext.Function.bind(this.refresh,this);this.info={width:0,height:0,contentWidth:0,contentHeight:0,flag:0};this.initElement();this.initConfig(a);this.bindListeners(true)},bindListeners:Ext.emptyFn,applyElement:function(a){if(a){return Ext.get(a)}},updateElement:function(a){a.append(this.detectorsContainer);a.addCls("x-size-monitored")},applyArgs:function(a){return a.concat([this.info])},refreshMonitors:Ext.emptyFn,forceRefresh:function(){Ext.TaskQueue.requestRead("refresh",this)},getContentBounds:function(){return this.detectorsContainer.getBoundingClientRect()},getContentWidth:function(){return this.detectorsContainer.offsetWidth},getContentHeight:function(){return this.detectorsContainer.offsetHeight},refreshSize:function(){var d=this.getElement();if(!d||d.destroyed){return false}var b=d.getWidth(),k=d.getHeight(),a=this.getContentWidth(),j=this.getContentHeight(),i=this.contentWidth,g=this.contentHeight,c=this.info,e=false,h;this.width=b;this.height=k;this.contentWidth=a;this.contentHeight=j;h=((i!==a?1:0)+(g!==j?2:0));if(h>0){c.width=b;c.height=k;c.contentWidth=a;c.contentHeight=j;c.flag=h;e=true;this.getCallback().apply(this.getScope(),this.getArgs())}return e},refresh:function(a){if(this.refreshSize()||a){Ext.TaskQueue.requestWrite("refreshMonitors",this)}},destroy:function(){var b=this,a=b.getElement();b.bindListeners(false);if(a&&!a.destroyed){a.removeCls("x-size-monitored")}delete b._element;b.callParent()}},1,0,0,0,0,[[Ext.mixin.Templatable.prototype.mixinId||Ext.mixin.Templatable.$className,Ext.mixin.Templatable]],[Ext.util.sizemonitor,"Abstract"],0));(Ext.cmd.derive("Ext.util.sizemonitor.Scroll",Ext.util.sizemonitor.Abstract,{getElementConfig:function(){return{reference:"detectorsContainer",classList:["x-size-monitors","scroll"],children:[{reference:"expandMonitor",className:"expand"},{reference:"shrinkMonitor",className:"shrink"}]}},constructor:function(a){this.onScroll=Ext.Function.bind(this.onScroll,this);Ext.util.sizemonitor.Abstract.prototype.constructor.apply(this,arguments)},bindListeners:function(b){var a=b?"addEventListener":"removeEventListener";this.expandMonitor[a]("scroll",this.onScroll,true);this.shrinkMonitor[a]("scroll",this.onScroll,true)},forceRefresh:function(){Ext.TaskQueue.requestRead("refresh",this,[true])},onScroll:function(){Ext.TaskQueue.requestRead("refresh",this)},refreshMonitors:function(){var b=this.expandMonitor,c=this.shrinkMonitor,a=1000000;if(b&&!b.destroyed){b.scrollLeft=a;b.scrollTop=a}if(c&&!c.destroyed){c.scrollLeft=a;c.scrollTop=a}}},1,0,0,0,0,0,[Ext.util.sizemonitor,"Scroll"],0));(Ext.cmd.derive("Ext.util.sizemonitor.OverflowChange",Ext.util.sizemonitor.Abstract,{constructor:function(a){this.onExpand=Ext.Function.bind(this.onExpand,this);this.onShrink=Ext.Function.bind(this.onShrink,this);Ext.util.sizemonitor.Abstract.prototype.constructor.apply(this,arguments)},getElementConfig:function(){return{reference:"detectorsContainer",classList:["x-size-monitors","overflowchanged"],children:[{reference:"expandMonitor",className:"expand",children:[{reference:"expandHelper"}]},{reference:"shrinkMonitor",className:"shrink",children:[{reference:"shrinkHelper"}]}]}},bindListeners:function(b){var a=b?"addEventListener":"removeEventListener";this.expandMonitor[a](Ext.browser.is.Firefox?"underflow":"overflowchanged",this.onExpand,true);this.shrinkMonitor[a](Ext.browser.is.Firefox?"overflow":"overflowchanged",this.onShrink,true)},onExpand:function(a){if(Ext.browser.is.Webkit&&a.horizontalOverflow&&a.verticalOverflow){return}Ext.TaskQueue.requestRead("refresh",this)},onShrink:function(a){if(Ext.browser.is.Webkit&&!a.horizontalOverflow&&!a.verticalOverflow){return}Ext.TaskQueue.requestRead("refresh",this)},refreshMonitors:function(){if(this.destroyed){return}var g=this.expandHelper,e=this.shrinkHelper,b=this.getContentBounds(),d=b.width,a=b.height,c;if(g&&!g.destroyed){c=g.style;c.width=(d+1)+"px";c.height=(a+1)+"px"}if(e&&!e.destroyed){c=e.style;c.width=d+"px";c.height=a+"px"}Ext.TaskQueue.requestRead("refresh",this)}},1,0,0,0,0,0,[Ext.util.sizemonitor,"OverflowChange"],0));(Ext.cmd.derive("Ext.util.SizeMonitor",Ext.Base,{constructor:function(a){var b=Ext.util.sizemonitor;if(Ext.browser.is.Firefox){return new b.OverflowChange(a)}else{return new b.Scroll(a)}}},1,0,0,0,0,0,[Ext.util,"SizeMonitor"],0));(Ext.cmd.derive("Ext.event.publisher.ElementSize",Ext.event.publisher.Publisher,{type:"size",handledEvents:["resize"],constructor:function(){this.monitors={};this.subscribers={};Ext.event.publisher.Publisher.prototype.constructor.apply(this,arguments)},subscribe:function(b){var d=b.id,c=this.subscribers,a=this.monitors;if(c[d]){++c[d]}else{c[d]=1;a[d]=new Ext.util.SizeMonitor({element:b,callback:this.onElementResize,scope:this,args:[b]})}b.on("painted","forceRefresh",a[d]);return true},unsubscribe:function(c){var e=c.id,d=this.subscribers,b=this.monitors,a;if(d[e]&&!--d[e]){delete d[e];a=b[e];c.un("painted","forceRefresh",a);a.destroy();delete b[e]}},onElementResize:function(a,b){Ext.TaskQueue.requestRead("fire",this,[a,"resize",[a,b]])}},1,0,0,0,0,0,[Ext.event.publisher,"ElementSize"],function(a){a.instance=new a()}));(Ext.cmd.derive("Ext.util.paintmonitor.Abstract",Ext.Base,{config:{element:null,callback:Ext.emptyFn,scope:null,args:[]},eventName:"",monitorClass:"",constructor:function(a){this.onElementPainted=Ext.Function.bind(this.onElementPainted,this);this.initConfig(a)},bindListeners:function(a){this.monitorElement[a?"addEventListener":"removeEventListener"](this.eventName,this.onElementPainted,true)},applyElement:function(a){if(a){return Ext.get(a)}},updateElement:function(a){this.monitorElement=Ext.Element.create({classList:["x-paint-monitor",this.monitorClass]},true);a.appendChild(this.monitorElement);a.addCls("x-paint-monitored");this.bindListeners(true)},onElementPainted:function(){},destroy:function(){var d=this,b=d.monitorElement,a=b.parentNode,c=d.getElement();d.bindListeners(false);delete d.monitorElement;if(c&&!c.destroyed){c.removeCls("x-paint-monitored");delete d._element}if(a){a.removeChild(b)}d.callParent()}},1,0,0,0,0,0,[Ext.util.paintmonitor,"Abstract"],0));(Ext.cmd.derive("Ext.util.paintmonitor.CssAnimation",Ext.util.paintmonitor.Abstract,{eventName:Ext.browser.is.WebKit?"webkitAnimationEnd":"animationend",monitorClass:"cssanimation",onElementPainted:function(a){if(a.animationName==="x-paint-monitor-helper"){this.getCallback().apply(this.getScope(),this.getArgs())}}},0,0,0,0,0,0,[Ext.util.paintmonitor,"CssAnimation"],0));(Ext.cmd.derive("Ext.util.PaintMonitor",Ext.Base,{constructor:function(a){return new Ext.util.paintmonitor.CssAnimation(a)}},1,0,0,0,0,0,[Ext.util,"PaintMonitor"],0));(Ext.cmd.derive("Ext.event.publisher.ElementPaint",Ext.event.publisher.Publisher,{type:"paint",handledEvents:["painted"],constructor:function(){this.monitors={};this.subscribers={};Ext.event.publisher.Publisher.prototype.constructor.apply(this,arguments)},subscribe:function(a){var b=this,d=a.id,c=b.subscribers;if(c[d]){++c[d]}else{c[d]=1;b.monitors[d]=new Ext.util.PaintMonitor({element:a,callback:b.onElementPainted,scope:b,args:[a]})}},unsubscribe:function(b){var d=b.id,c=this.subscribers,a=this.monitors;if(c[d]&&!--c[d]){delete c[d];a[d].destroy();delete a[d]}},onElementPainted:function(a){Ext.TaskQueue.requestRead("fire",this,[a,"painted",[a]])}},1,0,0,0,0,0,[Ext.event.publisher,"ElementPaint"],function(a){a.instance=new a()}));(Ext.cmd.derive("Ext.dom.Element",Ext.Base,function(q){var v=window,T=document,ah="ext-window",N="ext-document",j="width",R="height",ae="min-width",e="min-height",r="max-width",E="max-height",U="top",ai="right",O="bottom",I="left",l="visibility",af="hidden",d="display",W="none",o="z-index",Y="position",t="relative",u="static",A="-",y=/\w/g,M=/\s+/,ag=/[\s]+/,b=/^(?:transparent|(?:rgba[(](?:\s*\d+\s*[,]){3}\s*0\s*[)]))$/i,K=/table-row|table-.*-group/,c=/top/i,x={t:"border-top-width",r:"border-right-width",b:"border-bottom-width",l:"border-left-width"},ad={t:"padding-top",r:"padding-right",b:"padding-bottom",l:"padding-left"},n={t:"margin-top",r:"margin-right",b:"margin-bottom",l:"margin-left"},H=[ad.l,ad.r,ad.t,ad.b],h=[x.l,x.r,x.t,x.b],ab=/\d+$/,k=/\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,S="px",Q=/(-[a-z])/gi,aj=/([a-z0-9\-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,s=/^\d+(?:\.\d*)?px$/i,z={},w="originalDisplay",X=function(ak,al){return al.charAt(1).toUpperCase()},L=function(an,al){var ao,am,ak;if(an.nodeType===1){an._extData=null;if(al){ao=an.childNodes;for(am=0,ak=ao.length;amv.innerWidth)?"portrait":"landscape"},getViewportHeight:function(){var ak=q._viewportHeight;if(Ext.isIE9m){return T.documentElement.clientHeight}return(ak!=null)?ak:v.innerHeight},getViewportWidth:function(){var ak=q._viewportWidth;if(Ext.isIE9m){return T.documentElement.clientWidth}return(ak!=null)?ak:v.innerWidth},getViewSize:function(){return{width:q.getViewportWidth(),height:q.getViewportHeight()}},normalize:function(ak){return z[ak]||(z[ak]=ak.replace(Q,X))},_onWindowFocusChange:function(ak){if(Ext.fly(ak.target).is(q.editableSelector)){V=new Date();B=(ak.type==="focusin"||ak.type==="pointerup")}},_onWindowResize:function(){var ao=window.innerWidth,ap=window.innerHeight,an=new Date(),al=1000,am,ak;am=ao-q._windowWidth;ak=ap-q._windowHeight;q._windowWidth=ao;q._windowHeight=ap;if(((an-V)=q.minKeyboardHeight)){g=false;ac=new Date()}if(g){return}q._viewportWidth=ao;q._viewportHeight=ap},parseBox:function(am){am=am||0;var ak=typeof am,an,al;if(ak==="number"){return{top:am,right:am,bottom:am,left:am}}else{if(ak!=="string"){return am}}an=am.split(" ");al=an.length;if(al===1){an[1]=an[2]=an[3]=an[0]}else{if(al===2){an[2]=an[0];an[3]=an[1]}else{if(al===3){an[3]=an[1]}}}return{top:parseFloat(an[0])||0,right:parseFloat(an[1])||0,bottom:parseFloat(an[2])||0,left:parseFloat(an[3])||0}},parseStyles:function(al){var ak={},am;if(al){aj.lastIndex=0;while((am=aj.exec(al))){ak[am[1]]=am[2]||""}}return ak},select:function(ak,am,al){return Ext.fly(al||T).select(ak,am)},query:function(al,ak,am){return Ext.fly(am||T).query(al,ak)},unitizeBox:function(am,ak){var al=this;am=al.parseBox(am);return al.addUnits(am.top,ak)+" "+al.addUnits(am.right,ak)+" "+al.addUnits(am.bottom,ak)+" "+al.addUnits(am.left,ak)},serializeForm:function(am){var an=am.elements||(T.forms[am]||Ext.getDom(am)).elements,ax=false,aw=encodeURIComponent,aq="",ap=an.length,ar,ak,av,az,ay,at,ao,au,al;for(at=0;at0||am.scrollLeft!==0){an.push(am);ap.push(G.attach(am).getScroll())}}return function(){var at,au,ar;for(au=0,ar=an.length;au "+ak,!!al)},clone:function(ak,al){var am=this.dom.cloneNode(ak);if(Ext.supports.CloneNodeCopiesExpando){L(am,ak)}return al?am:Ext.get(am)},constrainScrollLeft:function(ak){var al=this.dom;return Math.max(Math.min(ak,al.scrollWidth-al.clientWidth),0)},constrainScrollTop:function(ak){var al=this.dom;return Math.max(Math.min(ak,al.scrollHeight-al.clientHeight),0)},createChild:function(al,ak,am){al=al||{tag:"div"};if(ak){return Ext.DomHelper.insertBefore(ak,al,am!==true)}else{return Ext.DomHelper.append(this.dom,al,am!==true)}},contains:function(ak){if(!ak){return false}var al=this,am=Ext.getDom(ak);return(am===al.dom)||al.isAncestor(am)},destroy:function(){var ak=this,al=ak.dom;if(al&&al.parentNode){al.parentNode.removeChild(al)}ak.collect();if(!ak.isFly){ak.callParent()}},detach:function(){var ak=this.dom;if(ak&&ak.parentNode&&ak.tagName!=="BODY"){ak.parentNode.removeChild(ak)}return this},disableShadow:function(){var ak=this.shadow;if(ak){ak.hide();ak.disabled=true}},disableShim:function(){var ak=this.shim;if(ak){ak.hide();ak.disabled=true}},disableTouchContextMenu:function(){this._contextMenuListenerRemover=this.on({MSHoldVisual:function(ak){ak.preventDefault()},destroyable:true,delegated:false})},disableTouchScroll:function(){this.addCls(aa);this.on({touchmove:function(ak){ak.preventDefault()},translate:false})},doReplaceWith:function(ak){var al=this.dom;al.parentNode.replaceChild(Ext.getDom(ak),al)},doScrollIntoView:function(ak,ao,al,ap,aw,at){G=G||new Ext.dom.Fly();var au=this,aq=au.dom,ax=G.attach(ak)[aw](),av=ak.scrollTop,ar=au.getScrollIntoViewXY(ak,ax,av),an=ar.x,am=ar.y;if(ap){if(al){al=Ext.apply({listeners:{afteranimate:function(){G.attach(aq).highlight()}}},al)}else{G.attach(aq).highlight()}}if(am!==av){G.attach(ak).scrollTo("top",am,al)}if(ao!==false&&(an!==ax)){G.attach(ak)[at]("left",an,al)}return au},down:function(ak,al){return this.selectNode(ak,!!al)},enableShadow:function(al,ak){var am=this,ao=am.shadow||(am.shadow=new Ext.dom.Shadow(Ext.apply({target:am},al))),an=am.shim;if(an){an.offsets=ao.outerOffsets;an.shadow=ao;ao.shim=an}if(ak===true||(ak!==false&&am.isVisible())){ao.show()}else{ao.hide()}ao.disabled=false},enableShim:function(al,ak){var am=this,ao=am.shim||(am.shim=new Ext.dom.Shim(Ext.apply({target:am},al))),an=am.shadow;if(an){ao.offsets=an.outerOffsets;ao.shadow=an;an.shim=ao}if(ak===true||(ak!==false&&am.isVisible())){ao.show()}else{ao.hide()}ao.disabled=false},findParent:function(aq,al,ak){var an=this,ao=an.dom,am=T.documentElement,ap=0;if(al||al===0){if(typeof al!=="number"){am=Ext.getDom(al);al=Number.MAX_VALUE}}else{al=50}while(ao&&ao.nodeType===1&&ap0&&ap<0.5){ak++}}}if(an){ak-=am.getBorderWidth("tb")+am.getPadding("tb")}return(ak<0)?0:ak},getHtml:function(){return this.dom?this.dom.innerHTML:""},getLeft:function(ak){return ak?this.getLocalX():this.getX()},getLocalX:function(){var am=this,al,ak=am.getStyle("left");if(!ak||ak==="auto"){ak=0}else{if(s.test(ak)){ak=parseFloat(ak)}else{ak=am.getX();al=am.dom.offsetParent;if(al){ak-=Ext.fly(al).getX()}}}return ak},getLocalXY:function(){var an=this,am,al=an.getStyle(["left","top"]),ak=al.left,ao=al.top;if(!ak||ak==="auto"){ak=0}else{if(s.test(ak)){ak=parseFloat(ak)}else{ak=an.getX();am=an.dom.offsetParent;if(am){ak-=Ext.fly(am).getX()}}}if(!ao||ao==="auto"){ao=0}else{if(s.test(ao)){ao=parseFloat(ao)}else{ao=an.getY();am=an.dom.offsetParent;if(am){ao-=Ext.fly(am).getY()}}}return[ak,ao]},getLocalY:function(){var al=this,ak,am=al.getStyle("top");if(!am||am==="auto"){am=0}else{if(s.test(am)){am=parseFloat(am)}else{am=al.getY();ak=al.dom.offsetParent;if(ak){am-=Ext.fly(ak).getY()}}}return am},getMargin:(function(){var al={t:"top",l:"left",r:"right",b:"bottom"},ak=["margin-top","margin-left","margin-right","margin-bottom"];return function(an){var ap=this,ao,am,aq;if(!an){ao=ap.getStyle(ak);aq={};if(ao&&typeof ao==="object"){aq={};for(am in n){aq[am]=aq[al[am]]=parseFloat(ao[n[am]])||0}}}else{aq=ap.addStyles(an,n)}return aq}})(),getPadding:function(ak){return this.addStyles(ak,ad)},getParent:function(){return Ext.get(this.dom.parentNode)},getRight:function(ak){return(ak?this.getLocalX():this.getX())+this.getWidth()},getScroll:function(){var am=this,ap=am.dom,al=T.documentElement,ao,an,ak=document.body;if(ap===T||ap===ak){ao=al.scrollLeft||(ak?ak.scrollLeft:0);an=al.scrollTop||(ak?ak.scrollTop:0)}else{ao=ap.scrollLeft;an=ap.scrollTop}return{left:ao,top:an}},getScrollIntoViewXY:function(ap,ax,aw){var az=this.dom,al=Ext.getDom(ap),am=this.getOffsetsTo(al),av=az.offsetWidth,ar=az.offsetHeight,ak=am[0]+ax,aq=am[1]+aw,ao=aq+ar,aB=ak+av,at=al.clientHeight,ay=al.clientWidth,aA=ax,au=aw,an=au+at,aC=aA+ay;if(ar>at||aqan){aw=ao-at}}if(av>ay||akaC){ax=aB-ay}}return{x:ax,y:aw}},getScrollLeft:function(){var ak=this.dom;if(ak===T||ak===document.body){return this.getScroll().left}else{return ak.scrollLeft}},getScrollTop:function(){var ak=this.dom;if(ak===T||ak===document.body){return this.getScroll().top}else{return ak.scrollTop}},getSize:function(ak){return{width:this.getWidth(ak),height:this.getHeight(ak)}},getStyle:function(ax,ar){var at=this,an=at.dom,aA=typeof ax!=="string",ay=at.styleHooks,al=ax,au=al,aq=1,ap,az,aw,av,am,ak,ao;if(aA){aw={};al=au[0];ao=0;if(!(aq=au.length)){return aw}}if(!an||an.documentElement){return aw||""}ap=an.style;if(ar){ak=ap}else{ak=an.ownerDocument.defaultView.getComputedStyle(an,null);if(!ak){ar=true;ak=ap}}do{av=ay[al];if(!av){ay[al]=av={name:q.normalize(al)}}if(av.get){am=av.get(an,at,ar,ak)}else{az=av.name;am=ak[az]}if(!aA){return am}aw[al]=am;al=au[++ao]}while(ao0&&ar<0.5){al++}}}if(ak){al-=an.getBorderWidth("lr")+an.getPadding("lr")}return(al<0)?0:al},getX:function(){return this.getXY()[0]},getXY:function(){var am=Math.round,ap=this.dom,al=0,aq=0,ao,ak;if(ap!==T&&ap!==T.body){try{ao=ap.getBoundingClientRect()}catch(an){ao={left:0,top:0}}al=am(ao.left);aq=am(ao.top);ak=Ext.getDoc().getScroll();al+=ak.left;aq+=ak.top}return[al,aq]},getY:function(){return this.getXY()[1]},getZIndex:function(){return parseInt(this.getStyle("z-index"),10)},hasCls:function(ak){var al=this.getData();if(!al.isSynchronized){this.synchronize()}return al.classMap.hasOwnProperty(ak)},hide:function(){this.setVisible(false);return this},insertAfter:function(ak){ak=Ext.getDom(ak);ak.parentNode.insertBefore(this.dom,ak.nextSibling);return this},insertBefore:function(ak){ak=Ext.getDom(ak);ak.parentNode.insertBefore(this.dom,ak);return this},insertFirst:function(al,ak){al=al||{};if(al.nodeType||al.dom||typeof al==="string"){al=Ext.getDom(al);this.dom.insertBefore(al,this.dom.firstChild);return !ak?Ext.get(al):al}else{return this.createChild(al,this.dom.firstChild,ak)}},insertHtml:function(al,am,ak){var an=Ext.DomHelper.insertHtml(al,this.dom,am);return ak?Ext.get(an):an},insertSibling:function(al,ao,ar){var aq=this,at=Ext.DomHelper,au=(ao||"before").toLowerCase()==="after",an,ak,am,ap;if(Ext.isIterable(al)){am=al.length;ak=Ext.fly(document.createDocumentFragment());if(Ext.isArray(al)){for(ap=0;apak.clientHeight||ak.scrollWidth>ak.clientWidth},isStyle:function(ak,al){return this.getStyle(ak)===al},isVisible:function(al){var am=this.dom,ak;if(!am){return false}if(!Z){Z=new Ext.dom.Fly()}for(ak=am.ownerDocument.documentElement;am!==ak;am=am.parentNode){if(!am||am.nodeType===11||(Z.attach(am)).isStyle(l,af)||Z.isStyle(d,W)){return false}if(!al){break}}return true},last:function(ak,al){return this.matchNode("previousSibling","lastChild",ak,al)},maskIframes:function(){var ak=document.getElementsByTagName("iframe");Ext.each(ak,function(an){var am=Ext.fly(an.parentNode),al=am.mask();al.setStyle("background-color","transparent")})},matchNode:function(al,ap,ak,am){var an=this.dom,ao;if(!an){return null}ao=an[ap];while(ao){if(ao.nodeType===1&&(!ak||Ext.fly(ao,"_matchNode").is(ak))){return !am?Ext.get(ao):ao}ao=ao[al]}return null},next:function(ak,al){return this.matchNode("nextSibling","nextSibling",ak,al)},parent:function(ak,al){return this.matchNode("parentNode","parentNode",ak,al)},position:function(ao,an,ak,am){var al=this;if(al.dom.tagName!=="BODY"){if(!ao&&al.isStyle(Y,u)){al.setStyle(Y,t)}else{if(ao){al.setStyle(Y,ao)}}if(an){al.setStyle(o,an)}if(ak||am){al.setXY([ak||false,am||false])}}},prev:function(ak,al){return this.matchNode("previousSibling","previousSibling",ak,al)},query:function(am,av,at){var ao=this.dom,aq,ar,au,al,ak,ap,an;if(!ao){return null}av=(av!==false);am=am.split(",");if(!at){aq=[]}for(ap=0,ar=am.length;ap0){al=an.className.split(ag);for(ao=0,ap=al.length;ao=":function(d){var c=this._filterValue;return this.getCandidateValue(d,c)>=c},">":function(d){var c=this._filterValue;return this.getCandidateValue(d,c)>c},"!=":function(d){var e=this,c=e._filterValue;d=e.getCandidateValue(d,c);if(e.isDateValue&&d instanceof Date){d=d.getTime();c=e.dateValue}return d!=c},"!==":function(d){var e=this,c=e._filterValue;d=e.getCandidateValue(d,c,true);if(e.isDateValue&&d instanceof Date){d=d.getTime();c=e.dateValue}return d!==c},"in":function(d){var c=this._filterValue;return Ext.Array.contains(c,this.getCandidateValue(d,c))},notin:function(d){var c=this._filterValue;return !Ext.Array.contains(c,this.getCandidateValue(d,c))},like:function(d){var c=this._filterValue;return c&&this.getCandidateValue(d,c).toLowerCase().indexOf(c.toLowerCase())>-1}});b["=="]=b["="];b.gt=b[">"];b.ge=b[">="];b.lt=b["<"];b.le=b["<="];b.eq=b["="];b.ne=b["!="]}));(Ext.cmd.derive("Ext.util.Observable",Ext.mixin.Observable,{$applyConfigs:true},0,0,0,0,0,0,[Ext.util,"Observable"],function(a){var b=Ext.mixin.Observable;a.releaseCapture=b.releaseCapture;a.capture=b.capture;a.captureArgs=b.captureArgs;a.observe=a.observeClass=b.observe}));(Ext.cmd.derive("Ext.util.AbstractMixedCollection",Ext.Base,{isMixedCollection:true,generation:0,indexGeneration:0,constructor:function(b,a){var c=this;if(arguments.length===1&&Ext.isObject(b)){c.initialConfig=b;Ext.apply(c,b)}else{c.allowFunctions=b===true;if(a){c.getKey=a}c.initialConfig={allowFunctions:c.allowFunctions,getKey:c.getKey}}c.items=[];c.map={};c.keys=[];c.indexMap={};c.length=0;c.mixins.observable.constructor.call(c)},destroy:function(){var a=this;a.items=a.map=a.keys=a.indexMap=null;a.callParent()},allowFunctions:false,add:function(c,d){var a=this.length,b;if(arguments.length===1){b=this.insert(a,c)}else{b=this.insert(a,c,d)}return b},getKey:function(a){return a.id},replace:function(c,e){var d=this,a,b;if(arguments.length==1){e=arguments[0];c=d.getKey(e)}a=d.map[c];if(typeof c=="undefined"||c===null||typeof a=="undefined"){return d.add(c,e)}d.generation++;b=d.indexOfKey(c);d.items[b]=e;d.map[c]=e;if(d.hasListeners.replace){d.fireEvent("replace",c,a,e)}return e},reorder:function(d){var h=this,b=h.items,c=0,g=b.length,a=[],e=[],i;h.suspendEvents();for(i in d){a[d[i]]=b[i]}for(c=0;c-1){b=e[g];delete e[g];delete c[g];e[h]=b;c[h]=a;d.keys[a]=h;d.indexGeneration=++d.generation}},addAll:function(c){var b=this,a;if(arguments.length>1||Ext.isArray(c)){b.insert(b.length,arguments.length>1?arguments:c)}else{for(a in c){if(c.hasOwnProperty(a)){if(b.allowFunctions||typeof c[a]!="function"){b.add(a,c[a])}}}}},each:function(e,d){var b=Ext.Array.push([],this.items),c=0,a=b.length,g;for(;c2){a=this.doInsert(b,[c],[d])}else{a=this.doInsert(b,[c])}a=a[0]}return a},doInsert:function(j,o,n){var l=this,b,c,g,k=o.length,a=k,e=l.hasListeners.add,d,h={},m,q,p;if(n!=null){l.useLinearSearch=true}else{n=o;o=new Array(k);for(g=0;g=0;--b){c.remove(a[b])}}else{while(c.length){c.removeAt(0)}}}else{c.length=c.items.length=c.keys.length=0;c.map={};c.indexMap={};c.generation++;c.indexGeneration=c.generation}},removeAt:function(a){var c=this,d,b;if(a=0){c.length--;d=c.items[a];Ext.Array.erase(c.items,a,1);b=c.keys[a];if(typeof b!="undefined"){delete c.map[b]}Ext.Array.erase(c.keys,a,1);if(c.hasListeners.remove){c.fireEvent("remove",d,b)}c.generation++;return d}return false},removeRange:function(h,a){var j=this,b,k,g,e,c,d;if(h=0){if(!a){a=1}e=Math.min(h+a,j.length);a=e-h;d=e===j.length;c=d&&j.indexGeneration===j.generation;for(g=h;g=0;a--){if(c[a]==null){d.removeAt(a)}}}else{return d.removeAt(d.indexOfKey(b))}},getCount:function(){return this.length},indexOf:function(c){var b=this,a;if(c!=null){if(!b.useLinearSearch&&(a=b.getKey(c))){return this.indexOfKey(a)}return Ext.Array.indexOf(b.items,c)}return -1},indexOfKey:function(a){if(!this.map.hasOwnProperty(a)){return -1}if(this.indexGeneration!==this.generation){this.rebuildIndexMap()}return this.indexMap[a]},rebuildIndexMap:function(){var e=this,d=e.indexMap={},c=e.keys,a=c.length,b;for(b=0;bb){e=true;g=i;i=b;b=g}if(i<0){i=0}if(b==null||b>=a){b=a-1}c=d.slice(i,b+1);if(e&&c.length){c.reverse()}return c},filter:function(d,c,e,a){var b=[];if(Ext.isString(d)){b.push(new Ext.util.Filter({property:d,value:c,anyMatch:e,caseSensitive:a}))}else{if(Ext.isArray(d)||d instanceof Ext.util.Filter){b=b.concat(d)}}return this.filterBy(Ext.util.Filter.createFilterFn(b))},filterBy:function(e,d){var j=this,a=new j.self(j.initialConfig),h=j.keys,b=j.items,g=b.length,c;a.getKey=j.getKey;for(c=0;ci)?1:(a0){c.removeRange(b.multiSortLimit,d)}break;case"prepend":c.insert(0,h);break;case"append":c.addAll(h);break;case undefined:case null:case"replace":c.clear();c.addAll(h);break;default:}}if(e!==false){b.fireEvent("beforesort",b,h);b.onBeforeSort(h);if(b.getSorterCount()){b.doSort(b.generateComparator())}}return h},getSorterCount:function(){return this.getSorters().items.length},generateComparator:function(){var a=this.getSorters().getRange();return a.length?this.createComparator(a):this.emptyComparator},emptyComparator:function(){return 0},onBeforeSort:Ext.emptyFn,decodeSorters:function(g){if(!Ext.isArray(g)){if(g===undefined){g=[]}else{g=[g]}}var d=g.length,h=Ext.util.Sorter,b=this.getModel?this.getModel():this.model,e,a,c;for(c=0;c>1;h=d(e,b[c]);if(h>=0){i=c+1}else{if(h<0){a=c-1}}}return i},reorder:function(a){Ext.util.AbstractMixedCollection.prototype.reorder.call(this,a);this.fireEvent("sort",this)},sortByKey:function(a,b){this._sort("key",a,b||function(d,c){var g=String(d).toUpperCase(),e=String(c).toUpperCase();return g>e?1:(gc){o=c}}}if(p){n.tasks=p}n.firing=false;if(n.tasks.length){n.startTimer(o-a,Ext.Date.now())}if(q!==false&&d.hasListeners.idle){d.fireEvent("idle")}},startTimer:function(e,c){var d=this,b=c+e,a=d.timerId;if(a&&d.nextExpires-b>d.interval){clearTimeout(a);a=null}if(!a){if(e=e.duration),g,i;if(a){b=e.duration;c=true}g=this.collectTargetData(e,b,h,c);if(h){e.target.setAttr(g.anims[e.id].attributes,true);d.collectTargetData(e,e.duration,h,c);e.paused=true;g=e.target.target;if(e.target.isComposite){g=e.target.target.last()}i={};i[Ext.supports.CSS3TransitionEnd]=e.lastFrame;i.scope=e;i.single=true;g.on(i)}return g},jumpToEnd:function(c){var b=this,d,a;if(!b.targetArr){b.targetArr={};a=true}d=b.runAnim(c,true);b.applyAnimAttrs(d,d.anims[c.id]);if(a){b.targetArr=null}},collectTargetData:function(c,a,e,g){var b=c.target.getId(),d=this.targetArr[b];if(!d){d=this.targetArr[b]={id:b,el:c.target,anims:{}}}d.anims[c.id]={id:c.id,anim:c,elapsed:a,isLastFrame:g,attributes:[{duration:c.duration,easing:(e&&c.reverse)?c.easingFn.reverse().toCSS3():c.easing,attrs:c.runAnim(a)}]};return d},applyAnimAttrs:function(c,a){var b=a.anim;if(a.attributes&&b.isRunning()){c.el.setAttr(a.attributes,false,a.isLastFrame);if(a.isLastFrame){b.lastFrame()}}},applyPendingAttrs:function(){var e=this.targetArr,g,c,b,d,a;for(c in e){if(e.hasOwnProperty(c)){g=e[c];for(a in g.anims){if(g.anims.hasOwnProperty(a)){b=g.anims[a];d=b.anim;if(b.attributes&&d.isRunning()){g.el.setAttr(b.attributes,false,b.isLastFrame);if(b.isLastFrame){d.lastFrame()}}}}}}}},1,0,0,0,0,[["queue",Ext.fx.Queue]],[Ext.fx,"Manager"],0));(Ext.cmd.derive("Ext.fx.Animator",Ext.Base,{isAnimator:true,duration:250,delay:0,delayStart:0,dynamic:false,easing:"ease",running:false,paused:false,damper:1,iterations:1,currentIteration:0,keyframeStep:0,animKeyFramesRE:/^(from|to|\d+%?)$/,constructor:function(a){var b=this;a=Ext.apply(b,a||{});b.config=a;b.id=Ext.id(null,"ext-animator-");b.mixins.observable.constructor.call(b,a);b.timeline=[];b.createTimeline(b.keyframes);if(b.target){b.applyAnimator(b.target);Ext.fx.Manager.addAnim(b)}},sorter:function(d,c){return d.pct-c.pct},createTimeline:function(d){var h=this,l=[],j=h.to||{},b=h.duration,m,a,c,g,k,e;for(k in d){if(d.hasOwnProperty(k)&&h.animKeyFramesRE.test(k)){e={attrs:Ext.apply(d[k],j)};if(k==="from"){k=0}else{if(k==="to"){k=100}}e.pct=parseInt(k,10);l.push(e)}}Ext.Array.sort(l,h.sorter);g=l.length;for(c=0;c0},isRunning:function(){return false}},1,0,0,0,0,[["observable",Ext.util.Observable]],[Ext.fx,"Animator"],0));(Ext.cmd.derive("Ext.fx.CubicBezier",Ext.Base,{singleton:true,cubicBezierAtTime:function(o,d,b,n,m,i){var j=3*d,l=3*(n-d)-j,a=1-j-l,h=3*b,k=3*(m-b)-h,p=1-h-k;function g(q){return((a*q+l)*q+j)*q}function c(q,s){var r=e(q,s);return((p*r+k)*r+h)*r}function e(q,y){var w,v,t,r,u,s;for(t=q,s=0;s<8;s++){r=g(t)-q;if(Math.abs(r)v){return v}while(wr){w=t}else{v=t}t=(v-w)/2+w}return t}return c(o,1/(200*i))},cubicBezier:function(b,e,a,c){var d=function(g){return Ext.fx.CubicBezier.cubicBezierAtTime(g,b,e,a,c,1)};d.toCSS3=function(){return"cubic-bezier("+[b,e,a,c].join(",")+")"};d.reverse=function(){return Ext.fx.CubicBezier.cubicBezier(1-a,1-c,1-b,1-e)};return d}},0,0,0,0,0,0,[Ext.fx,"CubicBezier"],0));(Ext.cmd.derive("Ext.fx.Easing",Ext.Base,function(){var e=Math,h=e.PI,d=e.pow,b=e.sin,g=e.sqrt,a=e.abs,c=1.70158;return{singleton:true,linear:Ext.identityFn,ease:function(r){var l=0.07813-r/2,k=g(0.0066+l*l),i=k-l,p=d(a(i),1/3)*(i<0?-1:1),o=-k-l,m=d(a(o),1/3)*(o<0?-1:1),j=p+m+0.25;return d(1-j,2)*3*j*0.1+(1-j)*3*j*j+j*j*j},easeIn:function(i){return d(i,1.7)},easeOut:function(i){return d(i,0.48)},easeInOut:function(r){var l=0.48-r/1.04,k=g(0.1734+l*l),i=k-l,p=d(a(i),1/3)*(i<0?-1:1),o=-k-l,m=d(a(o),1/3)*(o<0?-1:1),j=p+m+0.5;return(1-j)*3*j*j+j*j*j},backIn:function(i){return i*i*((c+1)*i-c)},backOut:function(i){i=i-1;return i*i*((c+1)*i+c)+1},elasticIn:function(k){if(k===0||k===1){return k}var j=0.3,i=j/4;return d(2,-10*k)*b((k-i)*(2*h)/j)+1},elasticOut:function(i){return 1-Ext.fx.Easing.elasticIn(1-i)},bounceIn:function(i){return 1-Ext.fx.Easing.bounceOut(1-i)},bounceOut:function(m){var j=7.5625,k=2.75,i;if(m<(1/k)){i=j*m*m}else{if(m<(2/k)){m-=(1.5/k);i=j*m*m+0.75}else{if(m<(2.5/k)){m-=(2.25/k);i=j*m*m+0.9375}else{m-=(2.625/k);i=j*m*m+0.984375}}}return i}}},0,0,0,0,0,0,[Ext.fx,"Easing"],function(b){var c=b.self,a=c.prototype;c.addMembers({"back-in":a.backIn,"back-out":a.backOut,"ease-in":a.easeIn,"ease-out":a.easeOut,"elastic-in":a.elasticIn,"elastic-out":a.elasticOut,"bounce-in":a.bounceIn,"bounce-out":a.bounceOut,"ease-in-out":a.easeInOut})}));(Ext.cmd.derive("Ext.fx.DrawPath",Ext.Base,{singleton:true,pathToStringRE:/,?([achlmqrstvxz]),?/gi,pathCommandRE:/([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,pathValuesRE:/(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,stopsRE:/^(\d+%?)$/,radian:Math.PI/180,is:function(b,a){a=String(a).toLowerCase();return(a=="object"&&b===Object(b))||(a=="undefined"&&typeof b==a)||(a=="null"&&b===null)||(a=="array"&&Array.isArray&&Array.isArray(b))||(Object.prototype.toString.call(b).toLowerCase().slice(8,-1))==a},path2string:function(){return this.join(",").replace(Ext.fx.DrawPath.pathToStringRE,"$1")},pathToString:function(a){return a.join(",").replace(Ext.fx.DrawPath.pathToStringRE,"$1")},parsePathString:function(a){if(!a){return null}var d={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0},c=[],b=this;if(b.is(a,"array")&&b.is(a[0],"array")){c=b.pathClone(a)}if(!c.length){String(a).replace(b.pathCommandRE,function(g,e,j){var i=[],h=e.toLowerCase();j.replace(b.pathValuesRE,function(l,k){if(k){i.push(+k)}});if(h=="m"&&i.length>2){c.push([e].concat(Ext.Array.splice(i,0,2)));h="l";e=(e=="m")?"l":"L"}while(i.length>=d[h]){c.push([e].concat(Ext.Array.splice(i,0,d[h])));if(!d[h]){break}}})}c.toString=b.path2string;return c},pathClone:function(g){var c=[],a,e,b,d;if(!this.is(g,"array")||!this.is(g&&g[0],"array")){g=this.parsePathString(g)}for(b=0,d=g.length;b7){p[s].shift();var t=p[s];while(t.length){Ext.Array.splice(p,s++,0,["C"].concat(Ext.Array.splice(t,0,6)))}Ext.Array.erase(p,s,1);o=Math.max(d.length,m.length||0)}},c=function(v,u,s,p,t){if(v&&u&&v[t][0]=="M"&&u[t][0]!="M"){Ext.Array.splice(u,t,0,["M",p.x,p.y]);s.bx=0;s.by=0;s.x=v[t][1];s.y=v[t][2];o=Math.max(d.length,m.length||0)}},h,o,g,q,e,k;for(h=0,o=Math.max(d.length,m.length||0);h1){aa=V(aa);H=aa*H;F=aa*F}c=H*H;R=F*F;U=(n==g?-1:1)*V(u((c*R-c*N*N-R*O*O)/(c*N*N+R*O*O)));C=U*H*N/F+(s+r)/2;B=U*-F*O/H+(af+ae)/2;m=o(((af-B)/F).toFixed(7));l=o(((ae-B)/F).toFixed(7));m=sl){m=m-d*2}if(!g&&l>m){l=l-d*2}}else{m=A[0];l=A[1];C=A[2];B=A[3]}q=l-m;if(u(q)>E){D=l;G=r;p=ae;l=m+E*(g&&l>m?1:-1);r=C+H*T(l);ae=B+F*a(l);M=v.arc2curve(r,ae,H,F,z,0,g,G,p,[l,D,C,B])}q=l-m;j=T(m);ad=a(m);e=T(l);ac=a(l);P=J.tan(q/4);S=4/3*H*P;Q=4/3*F*P;ab=[s,af];Z=[s+S*ad,af-Q*j];Y=[r+S*ac,ae-Q*e];W=[r,ae];Z[0]=2*ab[0]-Z[0];Z[1]=2*ab[1]-Z[1];if(A){return[Z,Y,W].concat(M)}else{M=[Z,Y,W].concat(M).join().split(",");L=[];K=M.length;for(X=0;X=d){l=d;a=true}if(i.reverse){l=d-l}for(e in k){if(k.hasOwnProperty(e)){j=k[e];h=a?1:c(l/d);g[e]=b[e].set(j,h)}}i.frameCount++;return g},lastFrame:function(){var c=this,a=c.iterations,b=c.currentIteration;b++;if(b0},isRunning:function(){return this.paused===false&&this.running===true&&this.isAnimator!==true}},1,0,0,0,0,[["observable",Ext.util.Observable]],[Ext.fx,"Anim"],0));Ext.enableFx=true;(Ext.cmd.derive("Ext.util.Animate",Ext.Base,{mixinId:"animate",isAnimate:true,animate:function(a){var b=this;if(Ext.fx.Manager.hasFxBlock(b.id)){return b}Ext.fx.Manager.queueFx(new Ext.fx.Anim(b.anim(a)));return this},anim:function(a){if(!Ext.isObject(a)){return(a)?{}:false}var b=this;if(a.stopAnimation){b.stopAnimation()}Ext.applyIf(a,Ext.fx.Manager.getFxDefaults(b.id));return Ext.apply({target:b,paused:true},a)},getAnimationProps:function(){var b=this,a=b.layout;return a&&a.animate?a.animate:{}},stopFx:Ext.Function.alias(Ext.util.Animate,"stopAnimation"),stopAnimation:function(){Ext.fx.Manager.stopAnimation(this.id);return this},syncFx:function(){Ext.fx.Manager.setFxDefaults(this.id,{concurrent:true});return this},sequenceFx:function(){Ext.fx.Manager.setFxDefaults(this.id,{concurrent:false});return this},hasActiveFx:Ext.Function.alias(Ext.util.Animate,"getActiveAnimation"),getActiveAnimation:function(){return Ext.fx.Manager.getActiveAnimation(this.id)}},0,0,0,0,0,0,[Ext.util,"Animate"],0));(Ext.cmd.derive("Ext.dom.Fly",Ext.dom.Element,{alternateClassName:"Ext.dom.Element.Fly",validNodeTypes:{1:1,9:1,11:1},isFly:true,constructor:function(a){this.dom=a;this.el=this},attach:function(b){var a=this;if(!b){return a.detach()}a.dom=b;if(!Ext.cache[b.id]){a.getData().isSynchronized=false}return a},detach:function(){this.dom=null},addListener:null,removeListener:null},1,0,0,0,0,0,[Ext.dom,"Fly",Ext.dom.Element,"Fly"],function(a){var b={};a.cache=b;Ext.fly=function(i,d){var g=null,e=Ext.fly,c,h;d=d||(e.caller&&e.caller.$name)||"_global";i=Ext.getDom(i);if(i){c=i.nodeType;if(a.prototype.validNodeTypes[c]||(!c&&(i.window==i))){g=Ext.cache[i.id];if(!g||g.dom!==i){g=b[d]||(b[d]=new a());g.dom=i;h=g.getData(true);if(h){h.isSynchronized=false}}}}return g}}));(Ext.cmd.derive("Ext.dom.CompositeElementLite",Ext.Base,{alternateClassName:["Ext.CompositeElementLite"],isComposite:true,isLite:true,statics:{importElementMethods:function(){var a=Ext.dom.Element,b=this.prototype;Ext.Object.each(a.prototype,function(c,d){if(typeof d==="function"&&!b[c]){b[c]=function(){return this.invoke(c,arguments)}}})}},constructor:function(b,a){if(a){this.elements=b||[]}else{this.elements=[];this.add(b)}},getElement:function(b){var a=this._fly||(this._fly=new Ext.dom.Fly());return a.attach(b)},transformElement:function(a){return Ext.getDom(a)},getCount:function(){return this.elements.length},add:function(c,a){var e=this.elements,b,d;if(!c){return this}if(typeof c=="string"){c=Ext.fly(a||document).query(c)}else{if(c.isComposite){c=c.elements}else{if(!Ext.isIterable(c)){c=[c]}}}for(b=0,d=c.length;b-1){c=Ext.getDom(c);if(a){g=this.elements[b];g.parentNode.insertBefore(c,g);Ext.removeNode(g)}Ext.Array.splice(this.elements,b,1,c)}return this},clear:function(d){var c=this,b=c.elements,a=b.length-1;if(d){for(;a>=0;a--){Ext.removeNode(b[a])}}this.elements=[]},addElements:function(d,b){if(!d){return this}if(typeof d==="string"){d=Ext.dom.Element.selectorFunction(d,b)}var c=this.elements,a=d.length,g;for(g=0;g','","",'",'"].join(""),J=/(?:]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,D=/(?:)((\n|\r|.)*?)(?:<\/script>)/ig,b=/\ssrc=([\'\"])(.*?)\1/i,o=/\S/,i=/\stype=([\'\"])(.*?)\1/i,A=/^-ms-/,B=/(-[a-z])/gi,I=function(Q,R){return R.charAt(1).toUpperCase()},n="x-masked",z="x-masked-relative",v="x-mask-msg",s=/^body/i,r={},w=function(R){var S=R.getData(),Q=S[H];if(Q===undefined){S[H]=Q=j.VISIBILITY}return Q},N=E.createRange?E.createRange():null,F={INPUT:true,TEXTAREA:true};if(Ext.isIE8){var h=E.createElement("div"),L=[],d=Ext.Function.createBuffered(function(){var Q=L.length,R;for(R=0;R"+Ext.String.format(P,Q)+""));R.selectNode("."+Q+"-mc").appendChild(this.dom);return R},clean:function(R){var T=this,V=T.dom,U=T.getData(),W=V.firstChild,S=-1,Q;if(U.isCleaned&&R!==true){return T}while(W){Q=W.nextSibling;if(W.nodeType===3){if(!(o.test(W.nodeValue))){V.removeChild(W)}else{if(Q&&Q.nodeType===3){W.appendData(Ext.String.trim(Q.data));V.removeChild(Q);Q=W.nextSibling;W.nodeIndex=++S}}}else{Ext.fly(W,"_clean").clean();W.nodeIndex=++S}W=Q}U.isCleaned=true;return T},empty:N?function(){var Q=this.dom;if(Q.firstChild){N.setStartBefore(Q.firstChild);N.setEndAfter(Q.lastChild);N.deleteContents()}}:function(){var Q=this.dom;while(Q.lastChild){Q.removeChild(Q.lastChild)}},clearListeners:function(){this.removeAnchor();arguments.callee.$previous.call(this)},clearPositioning:function(Q){Q=Q||"";return this.setStyle({left:Q,right:Q,top:Q,bottom:Q,"z-index":"",position:"static"})},createProxy:function(Q,U,T){Q=(typeof Q==="object")?Q:{tag:"div",role:"presentation",cls:Q};var S=this,R=U?Ext.DomHelper.append(U,Q,true):Ext.DomHelper.insertBefore(S.dom,Q,true);R.setVisibilityMode(j.DISPLAY);R.hide();if(T&&S.setBox&&S.getBox){R.setBox(S.getBox())}return R},clearOpacity:function(){return this.setOpacity("")},clip:function(){var R=this,S=R.getData(),Q;if(!S[q]){S[q]=true;Q=R.getStyle([K,u,t]);S[x]={o:Q[K],x:Q[u],y:Q[t]};R.setStyle(K,M);R.setStyle(u,M);R.setStyle(t,M)}return R},destroy:function(){var S=this,U=S.dom,T=S.getData(),R,Q;if(U&&S.isAnimate){S.stopAnimation()}arguments.callee.$previous.call(this);if(U&&Ext.isIE8&&(U.window!=U)&&(U.nodeType!==9)&&(U.tagName!=="BODY")&&(U.tagName!=="HTML")){L[L.length]=U;d()}if(T){R=T.maskEl;Q=T.maskMsg;if(R){R.destroy()}if(Q){Q.destroy()}}},enableDisplayMode:function(R){var Q=this;Q.setVisibilityMode(j.DISPLAY);if(R!==undefined){Q.getData()[m]=R}return Q},fadeIn:function(S){var Q=this,R=Q.dom;Q.animate(Ext.apply({},S,{opacity:1,internalListeners:{beforeanimate:function(U){var T=Ext.fly(R,"_anim");if(T.isStyle("display","none")){T.setDisplayed("")}else{T.show()}}}}));return this},fadeOut:function(S){var Q=this,R=Q.dom;S=Ext.apply({opacity:0,internalListeners:{afteranimate:function(U){if(R&&U.to.opacity===0){var T=Ext.fly(R,"_anim");if(S.useDisplay){T.setDisplayed(false)}else{T.hide()}}}}},S);Q.animate(S);return Q},fixDisplay:function(){var Q=this;if(Q.isStyle(a,G)){Q.setStyle(g,M);Q.setStyle(a,Q._getDisplay());if(Q.isStyle(a,G)){Q.setStyle(a,"block")}}},frame:function(Q,T,U){var S=this,V=S.dom,R;Q=Q||"#C3DAF9";T=T||1;U=U||{};R=function(){var Z=Ext.fly(V,"_anim"),Y=this,aa,X,W;Z.show();aa=Z.getBox();X=Ext.getBody().createChild({role:"presentation",id:Z.dom.id+"-anim-proxy",style:{position:"absolute","pointer-events":"none","z-index":35000,border:"0px solid "+Q}});W=new Ext.fx.Anim({target:X,duration:U.duration||1000,iterations:T,from:{top:aa.y,left:aa.x,borderWidth:0,opacity:1,height:aa.height,width:aa.width},to:{top:aa.y-20,left:aa.x-20,borderWidth:10,opacity:0,height:aa.height+40,width:aa.width+40}});W.on("afteranimate",function(){X.destroy();Y.end()})};S.animate({duration:(Math.max(U.duration,500)*2)||2000,listeners:{beforeanimate:{fn:R}},callback:U.callback,scope:U.scope});return S},getColor:function(R,S,X){var U=this.getStyle(R),T=X||X===""?X:"#",W,Q,V=0;if(!U||(/transparent|inherit/.test(U))){return S}if(/^r/.test(U)){U=U.slice(4,U.length-1).split(",");Q=U.length;for(;V5?T.toLowerCase():S)},getLoader:function(){var R=this,S=R.getData(),Q=S.loader;if(!Q){S.loader=Q=new Ext.ElementLoader({target:R})}return Q},getPositioning:function(R){var Q=this.getStyle(["left","top","position","z-index"]),S=this.dom;if(R){if(Q.left==="auto"){Q.left=S.offsetLeft+"px"}if(Q.top==="auto"){Q.top=S.offsetTop+"px"}}return Q},ghost:function(Q,T){var S=this,U=S.dom,R;Q=Q||"b";R=function(){var Y=Ext.fly(U,"_anim"),X=Y.getWidth(),W=Y.getHeight(),Z=Y.getXY(),V=Y.getPositioning(),aa={opacity:0};switch(Q){case"t":aa.y=Z[1]-W;break;case"l":aa.x=Z[0]-X;break;case"r":aa.x=Z[0]+X;break;case"b":aa.y=Z[1]+W;break;case"tl":aa.x=Z[0]-X;aa.y=Z[1]-W;break;case"bl":aa.x=Z[0]-X;aa.y=Z[1]+W;break;case"br":aa.x=Z[0]+X;aa.y=Z[1]+W;break;case"tr":aa.x=Z[0]+X;aa.y=Z[1]-W;break}this.to=aa;this.on("afteranimate",function(){var ab=Ext.fly(U,"_anim");if(ab){ab.hide();ab.clearOpacity();ab.setPositioning(V)}})};S.animate(Ext.applyIf(T||{},{duration:500,easing:"ease-out",listeners:{beforeanimate:R}}));return S},hide:function(Q){if(typeof Q==="string"){this.setVisible(false,Q);return this}this.setVisible(false,this.anim(Q));return this},highlight:function(T,R){var X=this,U=X.dom,Z={},W,aa,V,S,Q,Y;R=R||{};S=R.listeners||{};V=R.attr||"backgroundColor";Z[V]=T||"ffff9c";if(!R.to){aa={};aa[V]=R.endColor||X.getColor(V,"ffffff","")}else{aa=R.to}R.listeners=Ext.apply(Ext.apply({},S),{beforeanimate:function(){W=U.style[V];var ab=Ext.fly(U,"_anim");ab.clearOpacity();ab.show();Q=S.beforeanimate;if(Q){Y=Q.fn||Q;return Y.apply(Q.scope||S.scope||l,arguments)}},afteranimate:function(){if(U){U.style[V]=W}Q=S.afteranimate;if(Q){Y=Q.fn||Q;Y.apply(Q.scope||S.scope||l,arguments)}}});X.animate(Ext.apply({},R,{duration:1000,easing:"ease-in",from:Z,to:aa}));return X},hover:function(R,Q,T,S){var U=this;U.on("mouseenter",R,T||U.dom,S);U.on("mouseleave",Q,T||U.dom,S);return U},initDD:function(S,R,T){var Q=new Ext.dd.DD(Ext.id(this.dom),S,R);return Ext.apply(Q,T)},initDDProxy:function(S,R,T){var Q=new Ext.dd.DDProxy(Ext.id(this.dom),S,R);return Ext.apply(Q,T)},initDDTarget:function(S,R,T){var Q=new Ext.dd.DDTarget(Ext.id(this.dom),S,R);return Ext.apply(Q,T)},isFocusable:function(){var R=this.dom,Q=false,S;if(R&&!R.disabled){S=R.nodeName;Q=!!Ext.Element.naturallyFocusableTags[S]||((S==="A"||S==="LINK")&&!!R.href)||R.getAttribute("tabIndex")!=null||R.contentEditable==="true";if(Ext.isIE8&&S==="INPUT"&&R.type==="hidden"){Q=false}Q=Q&&this.isVisible(true)}return Q},isInputField:function(){var R=this.dom,Q=R.contentEditable;if((F[R.tagName]&&R.type!=="button")||(Q===""||Q==="true")){return true}return false},isTabbable:function(S){var T=this.dom,V=false,U,R,Q;if(T&&!T.disabled){U=T.nodeName;Q=T.getAttribute("tabIndex");R=Q!=null;Q-=0;if(U==="A"||U==="LINK"){if(T.href){V=R&&Q<0?false:true}else{if(T.contentEditable==="true"){V=!R||(R&&Q>=0)?true:false}else{V=R&&Q>=0?true:false}}}else{if(T.contentEditable==="true"||Ext.Element.naturallyTabbableTags[U]){V=R&&Q<0?false:true}else{if(R&&Q>=0){V=true}}}if(Ext.isIE8&&U==="INPUT"&&T.type==="hidden"){V=false}V=V&&(S||((!this.component||this.component.isVisible(true))&&this.isVisible(true)))}return V},isMasked:function(Q){var U=this,W=U.getData(),T=W.maskEl,R=W.maskMsg,V=false,S;if(T&&T.isVisible()){if(R){R.center(U)}V=true}else{if(Q){S=U.findParentNode();if(S){return Ext.fly(S).isMasked(Q)}}}return V},load:function(Q){this.getLoader().load(Q);return this},mask:function(X,V,Q){var T=this,W=T.dom,U=T.getData(),S=U.maskEl,R;if(!(s.test(W.tagName)&&T.getStyle("position")==="static")){T.addCls(z)}if(S){S.destroy()}S=Ext.DomHelper.append(W,{role:"presentation",cls:"x-mask x-border-box",children:{role:"presentation",cls:V?v+" "+V:v,cn:{tag:"div",role:"presentation",cls:"x-mask-msg-inner",cn:{tag:"div",role:"presentation",cls:"x-mask-msg-text",html:X||""}}}},true);R=Ext.get(S.dom.firstChild);U.maskEl=S;T.addCls(n);S.setDisplayed(true);if(typeof X==="string"){R.setDisplayed(true);R.center(T)}else{R.setDisplayed(false)}if(W===E.body){S.addCls("x-mask-fixed")}T.saveTabbableState({skipSelf:W===E.body});if(Ext.isIE9m&&W!==E.body&&T.isStyle("height","auto")){S.setSize(undefined,Q||T.getHeight())}return S},monitorMouseLeave:function(Q,T,S){var U=this,V,R={mouseleave:function(W){if(Ext.isIE9m){W.enableIEAsync()}V=Ext.defer(T,Q,S||U,[W])},mouseenter:function(){clearTimeout(V)}};U.on(R);return R},puff:function(U){var T=this,V=T.dom,R,S=T.getBox(),Q=T.getStyle(["width","height","left","right","top","bottom","position","z-index","font-size","opacity"],true);U=Ext.applyIf(U||{},{easing:"ease-out",duration:500,useDisplay:false});R=function(){var W=Ext.fly(V,"_anim");W.clearOpacity();W.show();this.to={width:S.width*2,height:S.height*2,x:S.x-(S.width/2),y:S.y-(S.height/2),opacity:0,fontSize:"200%"};this.on("afteranimate",function(){var X=Ext.fly(V,"_anim");if(X){if(U.useDisplay){X.setDisplayed(false)}else{X.hide()}X.setStyle(Q);Ext.callback(U.callback,U.scope)}})};T.animate({duration:U.duration,easing:U.easing,listeners:{beforeanimate:{fn:R}}});return T},selectable:function(){var Q=this;Q.dom.unselectable="";Q.removeCls(j.unselectableCls);Q.addCls(j.selectableCls);return Q},setCapture:function(){var Q=this.dom;if(Ext.isIE9m&&Q.setCapture){Q.setCapture()}},setHeight:function(Q,R){var S=this;if(!R||!S.anim){arguments.callee.$previous.apply(this,arguments)}else{if(!Ext.isObject(R)){R={}}S.animate(Ext.applyIf({to:{height:Q}},R))}return S},setHorizontal:function(){var R=this,Q=R.verticalCls;delete R.vertical;if(Q){delete R.verticalCls;R.removeCls(Q)}delete R.setWidth;delete R.setHeight;if(!Ext.isIE8){delete R.getWidth;delete R.getHeight}delete R.styleHooks},updateText:function(T){var Q=this,S,R;if(S){R=S.firstChild;if(!R||(R.nodeType!==3||R.nextSibling)){R=E.createTextNode();Q.empty();S.appendChild(R)}if(T){R.data=T}}},setHtml:function(S,R,X,T){var U=this,W,V,Q;if(!U.dom){return U}S=S||"";V=U.dom;if(R!==true){V.innerHTML=S;Ext.callback(X,U);return U}W=Ext.id();S+='';Q=Ext.interval(function(){var ae,ab,aa,Z,Y,ad,ac;if(!(ad=E.getElementById(W))){return false}clearInterval(Q);Ext.removeNode(ad);ae=Ext.getHead().dom;while((ab=J.exec(S))){aa=ab[1];Z=aa?aa.match(b):false;if(Z&&Z[2]){ac=E.createElement("script");ac.src=Z[2];Y=aa.match(i);if(Y&&Y[2]){ac.type=Y[2]}ae.appendChild(ac)}else{if(ab[2]&&ab[2].length>0){if(T){Ext.functionFactory(ab[2]).call(T)}else{Ext.globalEval(ab[2])}}}}Ext.callback(X,T||U)},20);V.innerHTML=S.replace(D,"");return U},setOpacity:function(R,Q){var S=this;if(!S.dom){return S}if(!Q||!S.anim){S.setStyle("opacity",R)}else{if(typeof Q!="object"){Q={duration:350,easing:"ease-in"}}S.animate(Ext.applyIf({to:{opacity:R}},Q))}return S},setPositioning:function(Q){return this.setStyle(Q)},setVertical:function(T,Q){var S=this,R=j.prototype;S.vertical=true;if(Q){S.addCls(S.verticalCls=Q)}S.setWidth=R.setHeight;S.setHeight=R.setWidth;if(!Ext.isIE8){S.getWidth=R.getHeight;S.getHeight=R.getWidth}S.styleHooks=(T===270)?R.verticalStyleHooks270:R.verticalStyleHooks90},setSize:function(S,Q,R){var T=this;if(Ext.isObject(S)){R=Q;Q=S.height;S=S.width}if(!R||!T.anim){T.dom.style.width=j.addUnits(S);T.dom.style.height=j.addUnits(Q);if(T.shadow||T.shim){T.syncUnderlays()}}else{if(R===true){R={}}T.animate(Ext.applyIf({to:{width:S,height:Q}},R))}return T},setVisible:function(U,Q){var S=this,T=S.dom,R=w(S);if(typeof Q==="string"){switch(Q){case a:R=j.DISPLAY;break;case g:R=j.VISIBILITY;break;case O:R=j.OFFSETS;break;case c:R=j.CLIP;break}S.setVisibilityMode(R);Q=false}if(!Q||!S.anim){if(R===j.DISPLAY){return S.setDisplayed(U)}else{if(R===j.OFFSETS){S[U?"removeCls":"addCls"](k)}else{if(R===j.CLIP){S[U?"removeCls":"addCls"](p)}else{if(R===j.VISIBILITY){S.fixDisplay();T.style.visibility=U?"":M}}}}}else{if(U){S.setOpacity(0.01);S.setVisible(true)}if(!Ext.isObject(Q)){Q={duration:350,easing:"ease-in"}}S.animate(Ext.applyIf({callback:function(){if(!U){Ext.fly(T).setVisible(false).setOpacity(1)}},to:{opacity:(U)?1:0}},Q))}S.getData()[y]=U;if(S.shadow||S.shim){S.setUnderlaysVisible(U)}return S},setWidth:function(R,Q){var S=this;if(!Q||!S.anim){arguments.callee.$previous.apply(this,arguments)}else{if(!Ext.isObject(Q)){Q={}}S.animate(Ext.applyIf({to:{width:R}},Q))}return S},setX:function(Q,R){return this.setXY([Q,this.getY()],R)},setXY:function(S,Q){var R=this;if(!Q||!R.anim){arguments.callee.$previous.call(this,S)}else{if(!Ext.isObject(Q)){Q={}}R.animate(Ext.applyIf({to:{x:S[0],y:S[1]}},Q))}return this},setY:function(R,Q){return this.setXY([this.getX(),R],Q)},show:function(Q){if(typeof Q==="string"){this.setVisible(true,Q);return this}this.setVisible(true,this.anim(Q));return this},slideIn:function(T,S,U){var W=this,R=W.dom,Z=R.style,Y,Q,V,X;T=T||"t";S=S||{};Y=function(){var ae=this,ad=S.listeners,ac=Ext.fly(R,"_anim"),af,aa,ag,ab;if(!U){ac.fixDisplay()}af=ac.getBox();if((T=="t"||T=="b")&&af.height===0){af.height=R.scrollHeight}else{if((T=="l"||T=="r")&&af.width===0){af.width=R.scrollWidth}}aa=ac.getStyle(["width","height","left","right","top","bottom","position","z-index"],true);ac.setSize(af.width,af.height);if(S.preserveScroll){V=ac.cacheScrollValues()}ab=ac.wrap({role:"presentation",id:Ext.id()+"-anim-wrap-for-"+ac.dom.id,style:{visibility:U?"visible":"hidden"}});X=ab.dom.parentNode;ab.setPositioning(ac.getPositioning());if(ab.isStyle("position","static")){ab.position("relative")}ac.clearPositioning("auto");ab.clip();if(V){V()}ac.setStyle({visibility:"",position:"absolute"});if(U){ab.setSize(af.width,af.height)}switch(T){case"t":ag={from:{width:af.width+"px",height:"0px"},to:{width:af.width+"px",height:af.height+"px"}};Z.bottom="0px";break;case"l":ag={from:{width:"0px",height:af.height+"px"},to:{width:af.width+"px",height:af.height+"px"}};W.anchorAnimX(T);break;case"r":ag={from:{x:af.x+af.width,width:"0px",height:af.height+"px"},to:{x:af.x,width:af.width+"px",height:af.height+"px"}};W.anchorAnimX(T);break;case"b":ag={from:{y:af.y+af.height,width:af.width+"px",height:"0px"},to:{y:af.y,width:af.width+"px",height:af.height+"px"}};break;case"tl":ag={from:{x:af.x,y:af.y,width:"0px",height:"0px"},to:{width:af.width+"px",height:af.height+"px"}};Z.bottom="0px";W.anchorAnimX("l");break;case"bl":ag={from:{y:af.y+af.height,width:"0px",height:"0px"},to:{y:af.y,width:af.width+"px",height:af.height+"px"}};W.anchorAnimX("l");break;case"br":ag={from:{x:af.x+af.width,y:af.y+af.height,width:"0px",height:"0px"},to:{x:af.x,y:af.y,width:af.width+"px",height:af.height+"px"}};W.anchorAnimX("r");break;case"tr":ag={from:{x:af.x+af.width,width:"0px",height:"0px"},to:{x:af.x,width:af.width+"px",height:af.height+"px"}};Z.bottom="0px";W.anchorAnimX("r");break}ab.show();Q=Ext.apply({},S);delete Q.listeners;Q=new Ext.fx.Anim(Ext.applyIf(Q,{target:ab,duration:500,easing:"ease-out",from:U?ag.to:ag.from,to:U?ag.from:ag.to}));Q.on("afteranimate",function(){var ah=Ext.fly(R,"_anim");ah.setStyle(aa);if(U){if(S.useDisplay){ah.setDisplayed(false)}else{ah.hide()}}if(ab.dom){if(ab.dom.parentNode){ab.dom.parentNode.insertBefore(ah.dom,ab.dom)}else{X.appendChild(ah.dom)}ab.destroy()}if(V){V()}ae.end()});if(ad){Q.on(ad)}};W.animate({duration:S.duration?Math.max(S.duration,500)*2:1000,listeners:{beforeanimate:Y}});return W},slideOut:function(Q,R){return this.slideIn(Q,R,true)},swallowEvent:function(R,S){var U=this,V,Q,T=function(W){W.stopPropagation();if(S){W.preventDefault()}};if(Ext.isArray(R)){Q=R.length;for(V=0;V0){W.setAttribute(X,++R)}else{if(W.hasAttribute("tabIndex")){W.setAttribute(T,W.getAttribute("tabIndex"))}else{W.setAttribute(T,"none")}W.setAttribute("tabIndex","-1");W.setAttribute(X,"1")}}return S},restoreTabbableState:function(Q,W){var U=this.dom,Y=Ext.Element.tabbableSavedCounterAttribute,Z=Ext.Element.tabbableSavedValueAttribute,S=[],aa,R,S,T,V,X;if(!U){return this}if(!W){S=Ext.Array.from(U.querySelectorAll("["+Y+"]"))}if(!Q){S.unshift(U)}for(V=0,X=S.length;V1){T.setAttribute(Y,--R);continue}aa=T.getAttribute(Z);if(aa==="none"){T.removeAttribute("tabIndex")}else{T.setAttribute("tabIndex",aa)}T.removeAttribute(Z);T.removeAttribute(Y)}return S}},deprecated:{"4.0":{methods:{pause:function(Q){var R=this;Ext.fx.Manager.setFxDefaults(R.id,{delay:Q});return R},scale:function(Q,R,S){this.animate(Ext.apply({},S,{width:Q,height:R}));return this},shift:function(Q){this.animate(Q);return this}}},"4.2":{methods:{moveTo:function(Q,S,R){return this.setXY([Q,S],R)},setBounds:function(R,U,T,Q,S){return this.setBox({x:R,y:U,width:T,height:Q},S)},setLeftTop:function(T,S){var R=this,Q=R.dom.style;Q.left=j.addUnits(T);Q.top=j.addUnits(S);if(R.shadow||R.shim){R.syncUnderlays()}return R},setLocation:function(Q,S,R){return this.setXY([Q,S],R)}}},"5.0":{methods:{getAttributeNS:function(R,Q){return this.getAttribute(Q,R)},getCenterXY:function(){return this.getAlignToXY(E,"c-c")},getComputedHeight:function(){return Math.max(this.dom.offsetHeight,this.dom.clientHeight)||parseFloat(this.getStyle(C))||0},getComputedWidth:function(){return Math.max(this.dom.offsetWidth,this.dom.clientWidth)||parseFloat(this.getStyle(e))||0},getStyleSize:function(){var U=this,V=this.dom,R=(V===E||V===E.body),T,Q,S;if(R){return{width:j.getViewportWidth(),height:j.getViewportHeight()}}T=U.getStyle(["height","width"],true);if(T.width&&T.width!=="auto"){Q=parseFloat(T.width)}if(T.height&&T.height!=="auto"){S=parseFloat(T.height)}return{width:Q||U.getWidth(true),height:S||U.getHeight(true)}},isBorderBox:function(){return true},isDisplayed:function(){return !this.isStyle("display","none")},focusable:"isFocusable"}}}}})(),function(){var q=Ext.dom.Element,p=q.prototype,w=!Ext.isIE8,b=document,m=b.defaultView,v=/alpha\(opacity=(.*)\)/i,h=/^\s+|\s+$/g,x=p.styleHooks,t=Ext.supports,e,o,d,s,g,y,c;p._init(q);delete p._init;Ext.plainTableCls="x-table-plain";Ext.plainListCls="x-list-plain";if(Ext.CompositeElementLite){Ext.CompositeElementLite.importElementMethods()}if(!t.Opacity&&Ext.isIE){Ext.apply(x.opacity,{get:function(B){var A=B.style.filter,z,k;if(A.match){z=A.match(v);if(z){k=parseFloat(z[1]);if(!isNaN(k)){return k?k/100:0}}}return 1},set:function(B,z){var k=B.style,A=k.filter.replace(v,"").replace(h,"");k.zoom=1;if(typeof(z)==="number"&&z>=0&&z<1){z*=100;k.filter=A+(A.length?" ":"")+"alpha(opacity="+z+")"}else{k.filter=A}}})}if(!t.matchesSelector){var j=/^([a-z]+|\*)?(?:\.([a-z][a-z\-_0-9]*))?$/i,l=/\-/g,a,u=function(k,z){var A=new RegExp("(?:^|\\s+)"+z.replace(l,"\\-")+"(?:\\s+|$)");if(k&&k!=="*"){k=k.toUpperCase();return function(B){return B.tagName===k&&A.test(B.className)}}return function(B){return A.test(B.className)}},r=function(k){k=k.toUpperCase();return function(z){return z.tagName===k}},n={};p.matcherCache=n;p.is=function(k){if(!k){return true}var z=this.dom,F,B,E,D,C,A,G;if(z.nodeType!==1){return false}if(!(E=Ext.isFunction(k)?k:n[k])){if(!(B=k.match(j))){D=z.parentNode;if(!D){C=true;D=a||(a=b.createDocumentFragment());a.appendChild(z)}A=Ext.Array.indexOf(Ext.fly(D,"_is").query(k),z)!==-1;if(C){a.removeChild(z)}return A}G=B[1];F=B[2];n[k]=E=F?u(G,F):r(G)}return E(z)}}if(!m||!m.getComputedStyle){p.getStyle=function(M,H){var I=this,D=I.dom,O=typeof M!=="string",A=M,J=A,G=1,B=H,z=I.styleHooks,N,F,L,K,C,k,E;if(O){L={};A=J[0];E=0;if(!(G=J.length)){return L}}if(!D||D.documentElement){return L||""}F=D.style;if(H){k=F}else{k=D.currentStyle;if(!k){B=true;k=F}}do{K=z[A];if(!K){z[A]=K={name:q.normalize(A)}}if(K.get){C=K.get(D,I,B,k)}else{N=K.name;C=k[N]}if(!O){return C}L[A]=C;A=J[++E]}while(E=9)){p.getAttribute=function(k,A){var B=this.dom,z;if(A){z=typeof B[A+":"+k];if(z!=="undefined"&&z!=="unknown"){return B[A+":"+k]||null}return null}if(k==="for"){k="htmlFor"}return B[k]||null}}Ext.onInternalReady(function(){var B=/^(?:transparent|(?:rgba[(](?:\s*\d+\s*[,]){3}\s*0\s*[)]))$/i,z=[],G=p.setWidth,H=p.setHeight,L=p.setSize,M=/^\d+(?:\.\d*)?px$/i,F,D,k,K;if(t.FixedTableWidthBug){x.width={name:"width",set:function(S,R,P){var O=S.style,N=P._needsTableWidthFix,Q=O.display;if(N){O.display="none"}O.width=R;if(N){S.scrollWidth;O.display=Q}}};p.setWidth=function(Q,O){var S=this,T=S.dom,P=T.style,N=S._needsTableWidthFix,R=P.display;if(N&&!O){P.display="none"}G.call(S,Q,O);if(N&&!O){T.scrollWidth;P.display=R}return S};p.setSize=function(R,O,P){var T=this,U=T.dom,Q=U.style,N=T._needsTableWidthFix,S=Q.display;if(N&&!P){Q.display="none"}L.call(T,R,O,P);if(N&&!P){U.scrollWidth;Q.display=S}return T}}if(Ext.isIE8){x.height={name:"height",set:function(S,R,P){var O=P.component,Q,N;if(O&&O._syncFrameHeight&&P===O.el){N=O.frameBody.dom.style;if(M.test(R)){Q=O.getFrameInfo();if(Q){N.height=(parseInt(R,10)-Q.height)+"px"}}else{if(!R||R==="auto"){N.height=""}}}S.style.height=R}};p.setHeight=function(N,P){var Q=this.component,R,O;if(Q&&Q._syncFrameHeight&&this===Q.el){O=Q.frameBody.dom.style;if(!N||N==="auto"){O.height=""}else{R=Q.getFrameInfo();if(R){O.height=(N-R.height)+"px"}}}return H.call(this,N,P)};p.setSize=function(R,N,P){var Q=this.component,S,O;if(Q&&Q._syncFrameHeight&&this===Q.el){O=Q.frameBody.dom.style;if(!N||N==="auto"){O.height=""}else{S=Q.getFrameInfo();if(S){O.height=(N-S.height)+"px"}}}return L.call(this,R,N,P)}}Ext.getDoc().on("selectstart",function(R,S){var Q=q.selectableCls,P=q.unselectableCls,N=S&&S.tagName;N=N&&N.toLowerCase();if(N==="input"||N==="textarea"){return}while(S&&S.nodeType===1&&S!==b.documentElement){var O=Ext.fly(S);if(O.hasCls(Q)){return}if(O.hasCls(P)){R.stopEvent();return}S=S.parentNode}});function E(R,O,Q,N){var P=N[this.name]||"";return B.test(P)?"transparent":P}function J(O,P,N){return function(){O.selectionStart=P;O.selectionEnd=N}}function I(R){var P=t.DisplayChangeInputSelectionBug,Q=t.DisplayChangeTextAreaSelectionBug,S,N,T,O;if(P||Q){S=q.getActiveElement();N=S&&S.tagName;if((Q&&N==="TEXTAREA")||(P&&N==="INPUT"&&S.type==="text")){if(Ext.fly(R).isAncestor(S)){T=S.selectionStart;O=S.selectionEnd;if(Ext.isNumber(T)&&Ext.isNumber(O)){return J(S,T,O)}}}}return Ext.emptyFn}function C(T,Q,S,P){var N=P.marginRight,O,R;if(N!=="0px"){O=T.style;R=O.display;O.display="inline-block";N=(S?P:T.ownerDocument.defaultView.getComputedStyle(T,null)).marginRight;O.display=R}return N}function A(U,R,T,Q){var N=Q.marginRight,P,O,S;if(N!=="0px"){P=U.style;O=I(U);S=P.display;P.display="inline-block";N=(T?Q:U.ownerDocument.defaultView.getComputedStyle(U,"")).marginRight;P.display=S;O()}return N}if(!t.RightMargin){x.marginRight=x["margin-right"]={name:"marginRight",get:(t.DisplayChangeInputSelectionBug||t.DisplayChangeTextAreaSelectionBug)?A:C}}if(!t.TransparentColor){F=["background-color","border-color","color","outline-color"];for(D=F.length;D--;){k=F[D];K=q.normalize(k);x[k]=x[K]={name:K,get:E}}}p.verticalStyleHooks90=e=Ext.Object.chain(x);p.verticalStyleHooks270=o=Ext.Object.chain(x);e.width=x.height||{name:"height"};e.height=x.width||{name:"width"};e["margin-top"]={name:"marginLeft"};e["margin-right"]={name:"marginTop"};e["margin-bottom"]={name:"marginRight"};e["margin-left"]={name:"marginBottom"};e["padding-top"]={name:"paddingLeft"};e["padding-right"]={name:"paddingTop"};e["padding-bottom"]={name:"paddingRight"};e["padding-left"]={name:"paddingBottom"};e["border-top"]={name:"borderLeft"};e["border-right"]={name:"borderTop"};e["border-bottom"]={name:"borderRight"};e["border-left"]={name:"borderBottom"};o.width=x.height||{name:"height"};o.height=x.width||{name:"width"};o["margin-top"]={name:"marginRight"};o["margin-right"]={name:"marginBottom"};o["margin-bottom"]={name:"marginLeft"};o["margin-left"]={name:"marginTop"};o["padding-top"]={name:"paddingRight"};o["padding-right"]={name:"paddingBottom"};o["padding-bottom"]={name:"paddingLeft"};o["padding-left"]={name:"paddingTop"};o["border-top"]={name:"borderRight"};o["border-right"]={name:"borderBottom"};o["border-bottom"]={name:"borderLeft"};o["border-left"]={name:"borderTop"};if(!Ext.scopeCss){z.push("x-body")}if(t.Touch){z.push("x-touch")}if(Ext.isIE&&Ext.isIE9m){z.push("x-ie","x-ie9m");z.push("x-ie8p");if(Ext.isIE8){z.push("x-ie8")}else{z.push("x-ie9","x-ie9p")}if(Ext.isIE8m){z.push("x-ie8m")}}if(Ext.isIE10){z.push("x-ie10")}if(Ext.isIE10p){z.push("x-ie10p")}if(Ext.isIE11){z.push("x-ie11")}if(Ext.isGecko){z.push("x-gecko")}if(Ext.isOpera){z.push("x-opera")}if(Ext.isOpera12m){z.push("x-opera12m")}if(Ext.isWebKit){z.push("x-webkit")}if(Ext.isSafari){z.push("x-safari")}if(Ext.isChrome){z.push("x-chrome")}if(Ext.isMac){z.push("x-mac")}if(Ext.isLinux){z.push("x-linux")}if(!t.CSS3BorderRadius){z.push("x-nbr")}if(!t.CSS3LinearGradient){z.push("x-nlg")}if(t.Touch){z.push("x-touch")}Ext.getBody().addCls(z)},null,{priority:1500})});(Ext.cmd.derive("Ext.GlobalEvents",Ext.mixin.Observable,{alternateClassName:"Ext.globalEvents",observableType:"global",singleton:true,resizeBuffer:100,idleEventMask:{mousemove:1,touchmove:1,pointermove:1,MSPointerMove:1,unload:1},constructor:function(){var a=this;a.callParent();Ext.onInternalReady(function(){a.attachListeners()})},attachListeners:function(){Ext.get(window).on("resize",this.fireResize,this,{buffer:this.resizeBuffer});Ext.getDoc().on("mousedown",this.fireMouseDown,this)},fireMouseDown:function(a){this.fireEvent("mousedown",a)},fireResize:function(){var d=this,b=Ext.Element,a=b.getViewportWidth(),c=b.getViewportHeight();if(d.curHeight!==c||d.curWidth!==a){d.curHeight=c;d.curWidth=a;d.fireEvent("resize",a,c)}}},1,0,0,0,0,0,[Ext,"GlobalEvents",Ext,"globalEvents"],function(a){Ext.on=function(){return a.addListener.apply(a,arguments)};Ext.un=function(){return a.removeListener.apply(a,arguments)}}));Ext.define("Ext.overrides.GlobalEvents",{override:"Ext.GlobalEvents",deprecated:{5:{methods:{addListener:function(d,h,i,j,c,b,e){var a,g;if(d==="ready"){g=h}else{if(typeof d!=="string"){for(a in d){if(a==="ready"){g=d[a]}}}}if(g){Ext.onReady(g)}this.callParent([d,h,i,j,c,b,e])}}}}});Ext.USE_NATIVE_JSON=false;Ext.JSON=(new (function(){var me=this,hasNative=window.JSON&&JSON.toString()==="[object JSON]",useHasOwn=!!{}.hasOwnProperty,pad=function(n){return n<10?"0"+n:n},doDecode=function(json){return eval("("+json+")")},doEncode=function(o,newline){if(o===null||o===undefined){return"null"}else{if(Ext.isDate(o)){return me.encodeDate(o)}else{if(Ext.isString(o)){if(Ext.isMSDate(o)){return me.encodeMSDate(o)}else{return me.encodeString(o)}}else{if(typeof o==="number"){return isFinite(o)?String(o):"null"}else{if(Ext.isBoolean(o)){return String(o)}else{if(o.toJSON){return o.toJSON()}else{if(Ext.isArray(o)){return encodeArray(o,newline)}else{if(Ext.isObject(o)){return encodeObject(o,newline)}else{if(typeof o==="function"){return"null"}}}}}}}}}return"undefined"},m={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\","\v":"\\u000b"},charToReplace=/[\\\"\x00-\x1f\x7f-\uffff]/g,encodeString=function(s){return'"'+s.replace(charToReplace,function(a){var c=m[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"'},encodeMSDate=function(o){return'"'+o+'"'},encodeArrayPretty=function(o,newline){var len=o.length,cnewline=newline+" ",sep=","+cnewline,a=["[",cnewline],i;for(i=0;i0}}return a},onBindNotify:function(b,a,c){c.syncing=(c.syncing+1)||1;this[c._config.names.set](b);--c.syncing},removeBindings:function(){var b=this,d,a,c;if(!b.destroying){d=b.getBind();if(d&&typeof d!=="string"){for(a in d){c=d[a];c.destroy();c._config=c.getTemplateScope=null}}}b.setBind(null)},updateSession:function(b){var a=this.getInherited();if(b){a.session=b}else{delete a.session}},updateViewModel:function(b){var c=this.getInherited(),a=this.getController();if(b){c.viewModel=b;b.setView(this);if(a){a.initViewModel(b)}}else{delete c.viewModel}}}},0,0,0,0,0,0,[Ext.mixin,"Bindable"],0));(Ext.cmd.derive("Ext.mixin.ComponentDelegation",Ext.Mixin,{mixinConfig:{id:"componentDelegation"},privates:{addDelegatedListener:function(g,i,k,l,d,c,e){var h=this,b,a,j;d=d||l.order;if(d){j=(l&&l.priority);if(!j){l=l?Ext.Object.chain(l):{};l.priority=h.$orderToPriority[d]}}b=h.$delegatedEvents||(h.$delegatedEvents={});a=b[g]||(b[g]=new Ext.util.Event(h,g));if(a.addListener(i,k,l,c,e)){h.$hasDelegatedListeners._incr_(g)}},clearDelegatedListeners:function(){var d=this,b=d.$delegatedEvents,a,c,e;if(b){for(a in b){c=b[a];e=c.listeners.length;c.clearListeners();d.$hasDelegatedListeners._decr_(a,e);delete b[a]}}},doFireDelegatedEvent:function(b,d){var h=this,c=true,a,e,g;if(h.$hasDelegatedListeners[b]){a=h.getRefOwner();while(a){e=a.$delegatedEvents;if(e){g=e[b];if(g){c=g.fireDelegated(h,d);if(c===false){break}}}a=a.getRefOwner()}}return c},removeDelegatedListener:function(a,d,c){var g=this,b=g.$delegatedEvents,e;if(b){e=b[a];if(e&&e.removeListener(d,c)){g.$hasDelegatedListeners._decr_(a);if(e.listeners.length===0){delete b[a]}}}}},onClassMixedIn:function(a){function b(){}a.prototype.HasListeners=a.HasListeners=b;b.prototype=a.hasListeners=new Ext.mixin.ComponentDelegation.HasDelegatedListeners()}},0,0,0,0,0,0,[Ext.mixin,"ComponentDelegation"],function(b){function a(){}b.HasDelegatedListeners=a;a.prototype=b.prototype.$hasDelegatedListeners=new Ext.mixin.Observable.HasListeners()}));(Ext.cmd.derive("Ext.Widget",Ext.Evented,{isWidget:true,element:{reference:"element"},observableType:"component",cachedConfig:{style:null},config:{userCls:null},eventedConfig:{width:null,height:null},template:[],constructor:function(b){var c=this,a;c.initId(b);c.initElement();c.mixins.observable.constructor.call(c,b);Ext.ComponentManager.register(c);a=c.getController();if(a){a.init(c)}},afterCachedConfig:function(){var j=this,k=j.self.prototype,l=j.referenceList,c=j.renderElement,g,d,e,h,b,a;k.renderTemplate=g=document.createDocumentFragment();g.appendChild(c.clone(true,true));a=g.querySelectorAll("[id]");for(e=0,h=a.length;e]+>/gi,stripScriptsRe:/(?:)((\n|\r|.)*?)(?:<\/script>)/ig,nl2brRe:/\r?\n/g,hashRe:/#+$/,allHashes:/^#+$/,formatPattern:/[\d,\.#]+/,formatCleanRe:/[^\d\.#]/g,I18NFormatCleanRe:null,formatFns:{},constructor:function(){a=this},undef:function(b){return b!==undefined?b:""},defaultValue:function(c,b){return c!==undefined&&c!==""?c:b},substr:"ab".substr(-1)!="b"?function(c,e,b){var d=String(c);return(e<0)?d.substr(Math.max(d.length+e,0),b):d.substr(e,b)}:function(c,d,b){return String(c).substr(d,b)},lowercase:function(b){return String(b).toLowerCase()},uppercase:function(b){return String(b).toUpperCase()},usMoney:function(b){return a.currency(b,"$",2)},currency:function(d,g,c,b){var j="",h=",0",e=0;d=d-0;if(d<0){d=-d;j="-"}c=Ext.isDefined(c)?c:a.currencyPrecision;h+=(c>0?".":"");for(;ec){b=b.substring(b.length-c)}}while(b.length2){}else{if(k.length===2){h=k[1].length;o=k[1].match(a.hashRe);if(o){i=o[0].length;d='trailingZeroes=new RegExp(Ext.String.escapeRegex(utilFormat.decimalSeparator) + "*0{0,'+i+'}$")'}}}b=["var utilFormat=Ext.util.Format,extNumber=Ext.Number,neg,absVal,fnum,parts"+(c?",thousandSeparator,thousands=[],j,n,i":"")+(l?',formatString="'+g+'",formatPattern=/[\\d,\\.#]+/':"")+',trailingZeroes;return function(v){if(typeof v!=="number"&&isNaN(v=extNumber.from(v,NaN)))return"";neg=v<0;',"absVal=Math.abs(v);","fnum=Ext.Number.toFixed(absVal, "+h+");",d,";"];if(c){if(h){b[b.length]='parts=fnum.split(".");';b[b.length]="fnum=parts[0];"}b[b.length]="if(absVal>=1000) {";b[b.length]="thousandSeparator=utilFormat.thousandSeparator;thousands.length=0;j=fnum.length;n=fnum.length%3||3;for(i=0;i")},capitalize:Ext.String.capitalize,uncapitalize:Ext.String.uncapitalize,ellipsis:Ext.String.ellipsis,escape:Ext.String.escape,escapeRegex:Ext.String.escapeRegex,htmlDecode:Ext.String.htmlDecode,htmlEncode:Ext.String.htmlEncode,leftPad:Ext.String.leftPad,toggle:Ext.String.toggle,trim:Ext.String.trim,parseBox:function(c){c=c||0;if(typeof c==="number"){return{top:c,right:c,bottom:c,left:c}}var d=c.split(" "),b=d.length;if(b===1){d[1]=d[2]=d[3]=d[0]}else{if(b===2){d[2]=d[0];d[3]=d[1]}else{if(b===3){d[3]=d[1]}}}return{top:parseInt(d[0],10)||0,right:parseInt(d[1],10)||0,bottom:parseInt(d[2],10)||0,left:parseInt(d[3],10)||0}}}},1,0,0,0,0,0,[Ext.util,"Format"],0));(Ext.cmd.derive("Ext.Template",Ext.Base,{inheritableStatics:{from:function(b,a){b=Ext.getDom(b);return new this(b.value||b.innerHTML,a||"")}},useEval:Ext.isGecko,constructor:function(d){var g=this,b=arguments,a=[],c,e=b.length,h;g.initialConfig={};if(e===1&&Ext.isArray(d)){b=d;e=b.length}if(e>1){for(c=0;c]*)\>)|(?:<\/tpl>)/g,actionsRe:/\s*(elif|elseif|if|for|foreach|exec|switch|case|eval|between)\s*\=\s*(?:(?:"([^"]*)")|(?:'([^']*)'))\s*/g,propRe:/prop=(?:(?:"([^"]*)")|(?:'([^']*)'))/,defaultRe:/^\s*default\s*$/,elseRe:/^\s*else\s*$/},1,0,0,0,0,0,[Ext.util,"XTemplateParser"],0));(Ext.cmd.derive("Ext.util.XTemplateCompiler",Ext.util.XTemplateParser,{useEval:Ext.isGecko,useIndex:Ext.isIE8m,useFormat:true,propNameRe:/^[\w\d\$]*$/,compile:function(a){var c=this,b=c.generate(a);return c.useEval?c.evalTpl(b):(new Function("Ext",b))(Ext)},generate:function(a){var d=this,b="var fm=Ext.util.Format,ts=Object.prototype.toString;",c;d.maxLevel=0;d.body=["var c0=values, a0="+d.createArrayTest(0)+", p0=parent, n0=xcount, i0=xindex, k0, v;\n"];if(d.definitions){if(typeof d.definitions==="string"){d.definitions=[d.definitions,b]}else{d.definitions.push(b)}}else{d.definitions=[b]}d.switches=[];d.parse(a);d.definitions.push((d.useEval?"$=":"return")+" function ("+d.fnArgs+") {",d.body.join(""),"}");c=d.definitions.join("\n");d.definitions.length=d.body.length=d.switches.length=0;delete d.definitions;delete d.body;delete d.switches;return c},doText:function(c){var b=this,a=b.body;c=c.replace(b.aposRe,"\\'").replace(b.newLineRe,"\\n");if(b.useIndex){a.push("out[out.length]='",c,"'\n")}else{a.push("out.push('",c,"')\n")}},doExpr:function(b){var a=this.body;a.push("if ((v="+b+") != null) out");if(this.useIndex){a.push("[out.length]=v+''\n")}else{a.push(".push(v+'')\n")}},doTag:function(a){var b=this.parseTag(a);if(b){this.doExpr(b)}else{this.doText("{"+a+"}")}},doElse:function(){this.body.push("} else {\n")},doEval:function(a){this.body.push(a,"\n")},doIf:function(b,c){var a=this;if(b==="."){a.body.push("if (values) {\n")}else{if(a.propNameRe.test(b)){a.body.push("if (",a.parseTag(b),") {\n")}else{a.body.push("if (",a.addFn(b),a.callFn,") {\n")}}if(c.exec){a.doExec(c.exec)}},doElseIf:function(b,c){var a=this;if(b==="."){a.body.push("else if (values) {\n")}else{if(a.propNameRe.test(b)){a.body.push("} else if (",a.parseTag(b),") {\n")}else{a.body.push("} else if (",a.addFn(b),a.callFn,") {\n")}}if(c.exec){a.doExec(c.exec)}},doSwitch:function(c){var b=this,a;if(c==="."||c==="#"){a=c==="."?"values":"xindex";b.body.push("switch (",a,") {\n")}else{if(b.propNameRe.test(c)){b.body.push("switch (",b.parseTag(c),") {\n")}else{b.body.push("switch (",b.addFn(c),b.callFn,") {\n")}}b.switches.push(0)},doCase:function(e){var d=this,c=Ext.isArray(e)?e:[e],g=d.switches.length-1,a,b;if(d.switches[g]){d.body.push("break;\n")}else{d.switches[g]++}for(b=0,g=c.length;b1){ out.push("',h.between,'"); } \n')}},doForEach:function(e,h){var d=this,c,b=d.level,a=b-1,g;if(e==="."){c="values"}else{if(d.propNameRe.test(e)){c=d.parseTag(e)}else{c=d.addFn(e)+d.callFn}}if(d.maxLevel1){ out.push("',h.between,'"); } \n')}},createArrayTest:("isArray" in Array)?function(a){return"Array.isArray(c"+a+")"}:function(a){return"ts.call(c"+a+')==="[object Array]"'},doExec:function(d,e){var c=this,a="f"+c.definitions.length,b=c.guards[c.strict?0:1];c.definitions.push("function "+a+"("+c.fnArgs+") {",b.doTry," var $v = values; with($v) {"," "+d," }",b.doCatch,"}");c.body.push(a+c.callFn+"\n")},guards:[{doTry:"",doCatch:""},{doTry:"try { ",doCatch:" } catch(e) {\n}"}],addFn:function(a){var d=this,b="f"+d.definitions.length,c=d.guards[d.strict?0:1];if(a==="."){d.definitions.push("function "+b+"("+d.fnArgs+") {"," return values","}")}else{if(a===".."){d.definitions.push("function "+b+"("+d.fnArgs+") {"," return parent","}")}else{d.definitions.push("function "+b+"("+d.fnArgs+") {",c.doTry," var $v = values; with($v) {"," return("+a+")"," }",c.doCatch,"}")}}return b},parseTag:function(b){var h=this,a=h.tagRe.exec(b),e,i,d,g,c;if(!a){return null}e=a[1];i=a[2];d=a[3];g=a[4];if(e=="."){if(!h.validTypes){h.definitions.push("var validTypes={string:1,number:1,boolean:1};");h.validTypes=true}c='validTypes[typeof values] || ts.call(values) === "[object Date]" ? values : ""'}else{if(e=="#"){c="xindex"}else{if(e=="$"){c="xkey"}else{if(e.substr(0,7)=="parent."){c=e}else{if(isNaN(e)&&e.indexOf("-")==-1&&e.indexOf(".")!=-1){c="values."+e}else{c="values['"+e+"']"}}}}}if(g){c="("+c+g+")"}if(i&&h.useFormat){d=d?","+d:"";if(i.substr(0,5)!="this."){i="fm."+i+"("}else{i+="("}}else{return c}return i+c+d+")"},evalTpl:function($){eval($);return $},newLineRe:/\r\n|\r|\n/g,aposRe:/[']/g,intRe:/^\s*(\d+)\s*$/,tagRe:/^([\w-\.\#\$]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\/]\s?[\d\.\+\-\*\/\(\)]+)?$/},0,0,0,0,0,0,[Ext.util,"XTemplateCompiler"],function(){var a=this.prototype;a.fnArgs="out,values,parent,xindex,xcount,xkey";a.callFn=".call(this,"+a.fnArgs+")"}));(Ext.cmd.derive("Ext.XTemplate",Ext.Template,{isXTemplate:true,emptyObj:{},fn:null,strict:false,apply:function(a,b,c,d){return this.applyOut(a,[],b,c,d).join("")},applyOut:function(a,b,d,h,j){var g=this,c;if(!g.fn){c=new Ext.util.XTemplateCompiler({useFormat:g.disableFormats!==true,definitions:g.definitions,strict:g.strict});g.fn=c.compile(g.html)}h=h||1;j=j||1;if(g.strict){g.fn(b,a,d||g.emptyObj,h,j)}else{try{g.fn(b,a,d||g.emptyObj,h,j)}catch(i){}}return b},compile:function(){return this},statics:{getTpl:function(b,d){var c=b[d],a;if(c&&!c.isTemplate){c=Ext.ClassManager.dynInstantiate("Ext.XTemplate",c);if(b.hasOwnProperty(d)){a=b}else{for(a=b.self.prototype;a&&!a.hasOwnProperty(d);a=a.superclass){}}a[d]=c;c.owner=a}return c||null}}},0,0,0,0,0,0,[Ext,"XTemplate"],0));(Ext.cmd.derive("Ext.app.EventDomain",Ext.Base,{statics:{instances:{}},isEventDomain:true,isInstance:false,constructor:function(){var a=this;if(!a.isInstance){Ext.app.EventDomain.instances[a.type]=a}a.bus={};a.monitoredClasses=[]},dispatch:function(j,n,l){n=Ext.canonicalEventName(n);var m=this,k=m.bus,o=k[n],e,c,b,d,p,h,g,a;if(!o){return true}for(e in o){if(o.hasOwnProperty(e)&&m.match(j,e,m.controller)){c=o[e];for(b in c){if(c.hasOwnProperty(b)){d=c[b];if(d.controller.isActive()){p=d.list;h=p.length;for(g=0;gd.clientWidth}}if(i==="scroll"){c=true}else{if(i){c=d.scrollHeight>d.clientHeight}}if(h){k=a.height}if(c){b=a.width}}}return{width:b,height:k}},getPosition:function(){return this.position},updateDirectionLock:Ext.emptyFn,updateDisabled:Ext.emptyFn,updateIndicators:Ext.emptyFn,updateMaxPosition:Ext.emptyFn,updateMaxUserPosition:Ext.emptyFn,updateMinPosition:Ext.emptyFn,updateMinUserPosition:Ext.emptyFn,updateMomenumEasing:Ext.emptyFn,updateX:Ext.emptyFn,updateY:Ext.emptyFn,onPartnerScrollStart:Ext.emptyFn,onPartnerScrollEnd:Ext.emptyFn,refresh:function(){this.fireEvent("refresh",this);return this},removePartner:function(b){var c=this._partners,a=b._partners;if(c){delete c[b.getId()]}if(a){delete (a[this.getId()])}},scrollBy:function(c,b,d){var a=this.getPosition();if(c){if(c.length){d=b;b=c[1];c=c[0]}else{if(typeof c!=="number"){d=b;b=c.y;c=c.x}}}c=(typeof c==="number")?c+a.x:null;b=(typeof b==="number")?b+a.y:null;return this.doScrollTo(c,b,d)},scrollIntoView:function(d,e,b,h){var j=this,i=j.getPosition(),g,a,k,c=j.getElement();if(d){g=Ext.fly(d).getScrollIntoViewXY(c,i.x,i.y);a=(e===false)?i.x:g.x;k=g.y;if(h){j.on({scrollend:"doHighlight",scope:j,single:true,args:[d,h]})}j.doScrollTo(a,k,b)}},isInView:function(c){var d=this,a={x:false,y:false},e,g=d.getElement(),b;if(c&&g.contains(c)){b=g.getRegion();e=Ext.fly(c).getRegion();a.x=e.right>b.left&&e.leftb.top&&e.top0){a-=1}if(g>0){g-=1}e.setSpacerXY({x:a,y:g});b.show()}}},deprecated:{"5":{methods:{getScroller:function(){return this}}},"5.1.0":{methods:{scrollToTop:function(a){return this.scrollTo(0,0,a)},scrollToEnd:function(a){return this.scrollTo(Infinity,Infinity,a)}}}},privates:{getSpacer:function(){var c=this,a=c._spacer,b;if(!a){b=c.getElement();a=c._spacer=b.createChild({cls:c._spacerCls,role:"presentation"});a.setVisibilityMode(2);b.position()}return a},applySpacerXY:function(b,a){if(a&&b.x===a.x&&b.y===a.y){b=undefined}return b},updateSpacerXY:function(a){this.getSpacer().setLocalXY(a.x,a.y)},convertX:function(a){return a},doHighlight:function(b,a){if(a!==true){Ext.fly(b).highlight(a)}else{Ext.fly(b).highlight()}},fireScrollStart:function(a,d){var c=this,b=c.component;c.invokePartners("onPartnerScrollStart",a,d);if(c.hasListeners.scrollstart){c.fireEvent("scrollstart",c,a,d)}if(b&&b.onScrollStart){b.onScrollStart(a,d)}Ext.GlobalEvents.fireEvent("scrollstart",c,a,d)},fireScroll:function(a,d){var c=this,b=c.component;c.invokePartners("onPartnerScroll",a,d);if(c.hasListeners.scroll){c.fireEvent("scroll",c,a,d)}if(b&&b.onScrollMove){b.onScrollMove(a,d)}Ext.GlobalEvents.fireEvent("scroll",c,a,d)},fireScrollEnd:function(a,d){var c=this,b=c.component;c.invokePartners("onPartnerScrollEnd",a,d);if(c.hasListeners.scrollend){c.fireEvent("scrollend",c,a,d)}if(b&&b.onScrollEnd){b.onScrollEnd(a,d)}Ext.GlobalEvents.fireEvent("scrollend",c,a,d)},initXStyle:function(){var b=this.getElement(),a=this.getX();if(b&&b.dom){if(!a){a="hidden"}else{if(a===true){a="auto"}}b.setStyle("overflow-x",a)}},initYStyle:function(){var a=this.getElement(),b=this.getY();if(a&&a.dom){if(!b){b="hidden"}else{if(b===true){b="auto"}}a.setStyle("overflow-y",b)}},invokePartners:function(i,a,h){var c=this,e=c._partners,b,g,d=i==="onPartnerScrollEnd";if(!c.suspendSync&!c.isReflecting){for(g in e){b=e[g].scroller;b.isReflecting=true;b[i](c,a,h);if(d){b.isReflecting=false}}}},clearReflecting:function(){this.isReflecting=false},suspendPartnerSync:function(){this.suspendSync=(this.suspendSync||0)+1},resumePartnerSync:function(){if(this.suspendSync){this.suspendSync--}},updateDomScrollPosition:function(){var d=this,c=d.getElement(),b,a=d.position;if(c&&!c.destroyed){b=d.getElementScroll(c);a.x=b.left;a.y=b.top}d.positionDirty=false;return a},getElementScroll:function(a){return a.getScroll()},onDomScroll:function(){var d=this,b,a,e,c;if(d.isTouchScroller&&Ext.supports.touchScroll===2){c=d.getElement().dom;c.scrollTop=c.scrollLeft=0;return}b=d.updateDomScrollPosition();a=b.x;e=b.y;if(!d.isScrolling){d.isScrolling=Ext.isScrolling=true;d.fireScrollStart(a,e)}d.fireScroll(a,e);d.onDomScrollEnd()},onDomScrollEnd:function(){var c=this,b=c.getPosition(),a=b.x,d=b.y;c.isScrolling=Ext.isScrolling=false;c.trackingScrollLeft=a;c.trackingScrollTop=d;c.fireScrollEnd(a,d)},onPartnerScroll:function(c,a,d){var b=c._partners[this.getId()].axis;if(b){if(b==="x"){d=null}else{if(b==="y"){a=null}}}this.doScrollTo(a,d,false,true)},restoreState:function(){var b=this,a=b.getElement(),c;if(a){c=a.dom;if(b.trackingScrollTop!==undefined){c.scrollTop=b.trackingScrollTop;c.scrollLeft=b.trackingScrollLeft}}}}},1,0,0,0,["scroller.scroller"],[[Ext.mixin.Factoryable.prototype.mixinId||Ext.mixin.Factoryable.$className,Ext.mixin.Factoryable]],[Ext.scroll,"Scroller"],0));(Ext.cmd.derive("Ext.fx.easing.Abstract",Ext.Base,{config:{startTime:0,startValue:0},isEasing:true,isEnded:false,constructor:function(a){this.initConfig(a);return this},applyStartTime:function(a){if(!a){a=Ext.Date.now()}return a},updateStartTime:function(a){this.reset()},reset:function(){this.isEnded=false},getValue:Ext.emptyFn},1,0,0,0,0,0,[Ext.fx.easing,"Abstract"],0));(Ext.cmd.derive("Ext.fx.easing.Momentum",Ext.fx.easing.Abstract,{config:{acceleration:30,friction:0,startVelocity:0},alpha:0,updateFriction:function(b){var a=Math.log(1-(b/10));this.theta=a;this.alpha=a/this.getAcceleration()},updateStartVelocity:function(a){this.velocity=a*this.getAcceleration()},updateAcceleration:function(a){this.velocity=this.getStartVelocity()*a;this.alpha=this.theta/a},getValue:function(){return this.getStartValue()-this.velocity*(1-this.getFrictionFactor())/this.theta},getFrictionFactor:function(){var a=Ext.Date.now()-this.getStartTime();return Math.exp(a*this.alpha)},getVelocity:function(){return this.getFrictionFactor()*this.velocity}},0,0,0,0,0,0,[Ext.fx.easing,"Momentum"],0));(Ext.cmd.derive("Ext.fx.easing.Bounce",Ext.fx.easing.Abstract,{config:{springTension:0.3,acceleration:30,startVelocity:0},getValue:function(){var b=Ext.Date.now()-this.getStartTime(),c=(b/this.getAcceleration()),a=c*Math.pow(Math.E,-this.getSpringTension()*c);return this.getStartValue()+(this.getStartVelocity()*a)}},0,0,0,0,0,0,[Ext.fx.easing,"Bounce"],0));(Ext.cmd.derive("Ext.fx.easing.BoundMomentum",Ext.fx.easing.Abstract,{config:{momentum:null,bounce:null,minMomentumValue:0,maxMomentumValue:0,minVelocity:0.01,startVelocity:0},applyMomentum:function(a,b){return Ext.factory(a,Ext.fx.easing.Momentum,b)},applyBounce:function(a,b){return Ext.factory(a,Ext.fx.easing.Bounce,b)},updateStartTime:function(a){this.getMomentum().setStartTime(a);Ext.fx.easing.Abstract.prototype.updateStartTime.apply(this,arguments)},updateStartVelocity:function(a){this.getMomentum().setStartVelocity(a)},updateStartValue:function(a){this.getMomentum().setStartValue(a)},reset:function(){this.lastValue=null;this.isBouncingBack=false;this.isOutOfBound=false;return Ext.fx.easing.Abstract.prototype.reset.apply(this,arguments)},getValue:function(){var a=this.getMomentum(),k=this.getBounce(),e=a.getStartVelocity(),g=e>0?1:-1,h=this.getMinMomentumValue(),d=this.getMaxMomentumValue(),c=(g==1)?d:h,i=this.lastValue,j,b;if(e===0){return this.getStartValue()}if(!this.isOutOfBound){j=a.getValue();b=a.getVelocity();if(Math.abs(b)=h&&j<=d){return j}this.isOutOfBound=true;k.setStartTime(Ext.Date.now()).setStartVelocity(b).setStartValue(c)}j=k.getValue();if(!this.isEnded){if(!this.isBouncingBack){if(i!==null){if((g==1&&ji)){this.isBouncingBack=true}}}else{if(Math.round(j)==c){this.isEnded=true}}}this.lastValue=j;return j}},0,0,0,0,0,0,[Ext.fx.easing,"BoundMomentum"],0));(Ext.cmd.derive("Ext.fx.easing.Linear",Ext.fx.easing.Abstract,{config:{duration:0,endValue:0},updateStartValue:function(a){this.distance=this.getEndValue()-a},updateEndValue:function(a){this.distance=a-this.getStartValue()},getValue:function(){var a=Ext.Date.now()-this.getStartTime(),b=this.getDuration();if(a>b){this.isEnded=true;return this.getEndValue()}else{return this.getStartValue()+((a/b)*this.distance)}}},0,0,0,0,["easing.linear"],0,[Ext.fx.easing,"Linear"],0));(Ext.cmd.derive("Ext.fx.easing.EaseOut",Ext.fx.easing.Linear,{config:{exponent:4,duration:1500},getValue:function(){var g=Ext.Date.now()-this.getStartTime(),d=this.getDuration(),b=this.getStartValue(),i=this.getEndValue(),a=this.distance,c=g/d,h=1-c,e=1-Math.pow(h,this.getExponent()),j=b+(e*a);if(g>=d){this.isEnded=true;return i}return j}},0,0,0,0,["easing.ease-out"],0,[Ext.fx.easing,"EaseOut"],0));(Ext.cmd.derive("Ext.util.translatable.Abstract",Ext.Evented,{config:{useWrapper:null,easing:null,easingX:null,easingY:null},x:0,y:0,activeEasingX:null,activeEasingY:null,isAnimating:false,isTranslatable:true,constructor:function(a){this.mixins.observable.constructor.call(this,a);this.position={x:0,y:0}},factoryEasing:function(a){return Ext.factory(a,Ext.fx.easing.Linear,null,"easing")},applyEasing:function(a){if(!this.getEasingX()){this.setEasingX(this.factoryEasing(a))}if(!this.getEasingY()){this.setEasingY(this.factoryEasing(a))}},applyEasingX:function(a){return this.factoryEasing(a)},applyEasingY:function(a){return this.factoryEasing(a)},doTranslate:Ext.emptyFn,translate:function(a,c,b){if(b){return this.translateAnimated(a,c,b)}if(this.isAnimating){this.stopAnimation()}if(!isNaN(a)&&typeof a=="number"){this.x=a}if(!isNaN(c)&&typeof c=="number"){this.y=c}this.doTranslate(a,c)},translateAxis:function(b,d,c){var a,e;if(b=="x"){a=d}else{e=d}return this.translate(a,e,c)},getPosition:function(){var b=this,a=b.position;a.x=-b.x;a.y=-b.y;return a},animate:function(b,a){this.activeEasingX=b;this.activeEasingY=a;this.isAnimating=true;this.lastX=null;this.lastY=null;Ext.AnimationQueue.start(this.doAnimationFrame,this);this.fireEvent("animationstart",this,this.x,this.y);return this},translateAnimated:function(b,i,g){var e=this;if(!Ext.isObject(g)){g={}}if(e.isAnimating){e.stopAnimation()}e.callback=g.callback;e.callbackScope=g.scope;var d=Ext.Date.now(),h=g.easing,c=(typeof b=="number")?(g.easingX||h||e.getEasingX()||true):null,a=(typeof i=="number")?(g.easingY||h||e.getEasingY()||true):null;if(c){c=e.factoryEasing(c);c.setStartTime(d);c.setStartValue(e.x);c.setEndValue(b);if("duration" in g){c.setDuration(g.duration)}}if(a){a=e.factoryEasing(a);a.setStartTime(d);a.setStartValue(e.y);a.setEndValue(i);if("duration" in g){a.setDuration(g.duration)}}return e.animate(c,a)},doAnimationFrame:function(){var e=this,c=e.activeEasingX,b=e.activeEasingY,d=Date.now(),a,g;if(!e.isAnimating){return}e.lastRun=d;if(c===null&&b===null){e.stopAnimation();return}if(c!==null){e.x=a=Math.round(c.getValue());if(c.isEnded){e.activeEasingX=null;e.fireEvent("axisanimationend",e,"x",a)}}else{a=e.x}if(b!==null){e.y=g=Math.round(b.getValue());if(b.isEnded){e.activeEasingY=null;e.fireEvent("axisanimationend",e,"y",g)}}else{g=e.y}if(e.lastX!==a||e.lastY!==g){e.doTranslate(a,g);e.lastX=a;e.lastY=g}e.fireEvent("animationframe",e,a,g)},stopAnimation:function(){var a=this;if(!a.isAnimating){return}a.activeEasingX=null;a.activeEasingY=null;a.isAnimating=false;Ext.AnimationQueue.stop(a.doAnimationFrame,a);a.fireEvent("animationend",a,a.x,a.y);if(a.callback){a.callback.call(a.callbackScope);a.callback=null}},refresh:function(){this.translate(this.x,this.y)},destroy:function(){if(this.isAnimating){this.stopAnimation()}Ext.Evented.prototype.destroy.call(this)}},1,0,0,0,0,0,[Ext.util.translatable,"Abstract"],0));(Ext.cmd.derive("Ext.util.translatable.Dom",Ext.util.translatable.Abstract,{config:{element:null},applyElement:function(a){if(!a){return}return Ext.get(a)},updateElement:function(){this.refresh()}},0,0,0,0,0,0,[Ext.util.translatable,"Dom"],0));(Ext.cmd.derive("Ext.util.translatable.CssTransform",Ext.util.translatable.Dom,{doTranslate:function(a,c){var b=this.getElement();if(!this.destroyed&&!b.destroyed){b.translate(a,c)}},destroy:function(){var a=this.getElement();if(a&&!a.destroyed){a.dom.style.webkitTransform=null}Ext.util.translatable.Dom.prototype.destroy.call(this)}},0,0,0,0,0,0,[Ext.util.translatable,"CssTransform"],0));(Ext.cmd.derive("Ext.util.translatable.ScrollPosition",Ext.util.translatable.Dom,{type:"scrollposition",config:{useWrapper:true},getWrapper:function(){var c=this.wrapper,b=this.getElement(),a;if(!c){a=b.getParent();if(!a){return null}if(a.hasCls("x-translatable-hboxfix")){a=a.getParent()}if(this.getUseWrapper()){c=b.wrap()}else{c=a}b.addCls("x-translatable");c.addCls("x-translatable-container");this.wrapper=c;c.on("painted",function(){if(!this.isAnimating){this.refresh()}},this);this.refresh()}return c},doTranslate:function(a,d){var c=this.getWrapper(),b;if(c){b=c.dom;if(typeof a=="number"){b.scrollLeft=500000-a}if(typeof d=="number"){b.scrollTop=500000-d}}},destroy:function(){var b=this,a=b.getElement(),c=b.wrapper;if(c){if(!a.destroyed){if(b.getUseWrapper()){c.doReplaceWith(a)}a.removeCls("x-translatable")}if(!c.destroyed){c.removeCls("x-translatable-container");c.un("painted","refresh",b)}delete b.wrapper;delete b._element}Ext.util.translatable.Dom.prototype.destroy.call(this)}},0,0,0,0,0,0,[Ext.util.translatable,"ScrollPosition"],0));(Ext.cmd.derive("Ext.util.translatable.ScrollParent",Ext.util.translatable.Dom,{isScrollParent:true,applyElement:function(a){var b=Ext.get(a);if(b){this.parent=b.parent()}return b},doTranslate:function(a,c){var b=this.parent;b.setScrollLeft(Math.round(-a));b.setScrollTop(Math.round(-c))},getPosition:function(){var c=this,a=c.position,b=c.parent;a.x=b.getScrollLeft();a.y=b.getScrollTop();return a}},0,0,0,0,0,0,[Ext.util.translatable,"ScrollParent"],0));(Ext.cmd.derive("Ext.util.translatable.CssPosition",Ext.util.translatable.Dom,{doTranslate:function(a,c){var b=this.getElement().dom.style;if(typeof a=="number"){b.left=a+"px"}if(typeof c=="number"){b.top=c+"px"}},destroy:function(){var a=this.getElement().dom.style;a.left=null;a.top=null;Ext.util.translatable.Dom.prototype.destroy.call(this)}},0,0,0,0,0,0,[Ext.util.translatable,"CssPosition"],0));(Ext.cmd.derive("Ext.util.Translatable",Ext.Base,{constructor:function(a){var b=Ext.util.translatable;switch(Ext.browser.getPreferredTranslationMethod(a)){case"scrollposition":return new b.ScrollPosition(a);case"scrollparent":return new b.ScrollParent(a);case"csstransform":return new b.CssTransform(a);case"cssposition":return new b.CssPosition(a)}}},1,0,0,0,0,0,[Ext.util,"Translatable"],0));(Ext.cmd.derive("Ext.scroll.Indicator",Ext.Widget,{config:{axis:null,hideAnimation:true,hideDelay:0,scroller:null,minLength:24},defaultHideAnimation:{to:{opacity:0},duration:300},names:{x:{side:"l",getSize:"getHeight",setLength:"setWidth",translate:"translateX"},y:{side:"t",getSize:"getWidth",setLength:"setHeight",translate:"translateY"}},oppositeAxis:{x:"y",y:"x"},cls:"x-scroll-indicator",applyHideAnimation:function(a){if(a){a=Ext.mergeIf({onEnd:this.onHideAnimationEnd,scope:this},this.defaultHideAnimation,a)}return a},constructor:function(a){var c=this,b;Ext.Widget.prototype.constructor.call(this,a);b=c.getAxis();c.names=c.names[b];c.element.addCls(c.cls+" "+c.cls+"-"+b)},hide:function(){var b=this,a=b.getHideDelay();if(a){b._hideTimer=Ext.defer(b.doHide,a,b)}else{b.doHide()}},setValue:function(n){var l=this,c=l.element,k=l.names,d=l.getAxis(),i=l.getScroller(),h=i.getMaxUserPosition()[d],o=i.getElementSize()[d],j=l.length,b=l.getMinLength(),a=j,g=o-j-l.sizeAdjust,p=Math.round,m=Math.max,e;if(n<0){a=p(m(j+(j*n/o),b));e=0}else{if(n>h){a=p(m(j-(j*(n-h)/o),b));e=g+j-a}else{e=p(n/h*g)}}l[k.translate](e);c[k.setLength](a)},show:function(){var b=this,a=b.element,c=a.getActiveAnimation();if(c){c.end()}if(!b._inDom){b.getScroller().getElement().appendChild(a);b._inDom=true;if(!b.size){b.cacheStyles()}}b.refreshLength();clearTimeout(b._hideTimer);a.setStyle("opacity","")},privates:{cacheStyles:function(){var b=this,a=b.element,c=b.names;b.size=a[c.getSize]();b.margin=a.getMargin(c.side)},doHide:function(){var b=this.getHideAnimation(),a=this.element;if(b){a.animate(b)}else{a.setStyle("opacity",0)}},hasOpposite:function(){return this.getScroller().isAxisEnabled(this.oppositeAxis[this.getAxis()])},onHideAnimationEnd:function(){this.element.setStyle("opacity","0")},refreshLength:function(){var j=this,i=j.names,d=j.getAxis(),g=j.getScroller(),a=g.getSize()[d],k=g.getElementSize()[d],h=k/a,c=j.margin*2,e=j.hasOpposite()?(c+j.size):c,b=Math.max(Math.round((k-e)*h),j.getMinLength());j.sizeAdjust=e;j.length=b;j.element[i.setLength](b)},translateX:function(a){this.element.translate(a)},translateY:function(a){this.element.translate(0,a)}}},1,["scrollindicator"],["widget","scrollindicator"],{widget:true,scrollindicator:true},["widget.scrollindicator"],0,[Ext.scroll,"Indicator"],0));(Ext.cmd.derive("Ext.scroll.TouchScroller",Ext.scroll.Scroller,{isTouchScroller:true,config:{autoRefresh:true,bounceEasing:{duration:400},elementSize:undefined,indicators:true,fps:"auto",maxAbsoluteVelocity:6,momentumEasing:{momentum:{acceleration:30,friction:0.5},bounce:{acceleration:30,springTension:0.3},minVelocity:1},outOfBoundRestrictFactor:0.5,innerElement:null,size:undefined,slotSnapEasing:{duration:150},slotSnapOffset:{x:0,y:0},startMomentumResetTime:300,translatable:{translationMethod:"auto",useWrapper:false}},cls:"x-scroll-container",scrollerCls:"x-scroll-scroller",dragStartTime:0,dragEndTime:0,isDragging:false,isAnimating:false,isMouseEvent:{mousedown:1,mousemove:1,mouseup:1},listenerMap:{touchstart:"onTouchStart",touchmove:"onTouchMove",dragstart:"onDragStart",drag:"onDrag",dragend:"onDragEnd"},refreshCounter:0,constructor:function(a){var b=this,c="onEvent";b.elementListeners={touchstart:c,touchmove:c,dragstart:c,drag:c,dragend:c,scope:b};b.minPosition={x:0,y:0};b.startPosition={x:0,y:0};b.velocity={x:0,y:0};b.isAxisEnabledFlags={x:false,y:false};b.flickStartPosition={x:0,y:0};b.flickStartTime={x:0,y:0};b.lastDragPosition={x:0,y:0};b.dragDirection={x:0,y:0};Ext.scroll.Scroller.prototype.constructor.call(this,a);b.refreshAxes();b.scheduleRefresh={idle:b.doRefresh,scope:b,single:true,destroyable:true}},applyBounceEasing:function(b){var a=Ext.fx.easing.EaseOut;return{x:Ext.factory(b,a),y:Ext.factory(b,a)}},applyElementSize:function(b){var c=this.getElement(),d,a,e;if(!c){return null}d=c.dom;if(!d){return}if(b==null){a=d.clientWidth;e=d.clientHeight}else{a=b.x;e=b.y}return{x:a,y:e}},applyIndicators:function(g,c){var e=this,b,d,a,h;if(g){if(g===true){b=d={}}else{a=g.x;h=g.y;if(a||h){b=(a==null||a===true)?{}:a;d=(a==null||h===true)?{}:h}else{b=d=g}}if(c){if(b){c.x.setConfig(b)}else{c.x.destroy();c.x=null}if(d){c.y.setConfig(d)}else{c.y.destroy();c.y=null}g=c}else{g={x:null,y:null};if(b){g.x=new Ext.scroll.Indicator(Ext.applyIf({axis:"x",scroller:e},b))}if(d){g.y=new Ext.scroll.Indicator(Ext.applyIf({axis:"y",scroller:e},d))}}}else{if(c){if(c.x){c.x.destroy()}if(c.y){c.y.destroy()}c.x=c.y=null}}return g},applyMomentumEasing:function(b){var a=Ext.fx.easing.BoundMomentum;return{x:Ext.factory(b,a),y:Ext.factory(b,a)}},applyInnerElement:function(a){if(a&&!a.isElement){a=Ext.get(a)}return a},applyMaxPosition:function(c,d){if(d&&c.x===d.x&&c.y===d.y){return}var a=this.getTranslatable(),b;if(a.isAnimating){b=a.activeEasingY;if(b&&b.getStartVelocity&&b.getStartVelocity()<0&&c.yk){g=k}}if(o){h=n.convertX(h);g=n.convertX(g)}if(g!==null){m=n.getBounceEasing()[i];m.setConfig({startTime:c,startValue:-h,endValue:-g});return m}if(j===0){return null}if(j<-b){j=-b}else{if(j>b){j=b}}m=n.getMomentumEasing()[i];a={startTime:c,startValue:-h,startVelocity:j*1.5,minMomentumValue:-k,maxMomentumValue:0};if(o){n.convertEasingConfig(a)}m.setConfig(a);return m},getSnapPosition:function(c){var d=this,i=d.getSlotSnapSize()[c],e=null,a,h,g,b;if(i!==0&&d.isAxisEnabled(c)){a=d.position[c];h=d.getSlotSnapOffset()[c];g=d.getMaxUserPosition()[c];b=Math.floor((a-h)%i);if(b!==0){if(a!==g){if(Math.abs(b)>i/2){e=Math.min(g,a+((b>0)?i-b:b-i))}else{e=a-b}}else{e=a-b}}}return e},hideIndicators:function(){var c=this,d=c.getIndicators(),a,b;if(d){if(c.isAxisEnabled("x")){a=d.x;if(a){a.hide()}}if(c.isAxisEnabled("y")){b=d.y;if(b){b.hide()}}}},isAxisEnabled:function(a){this.getX();this.getY();return this.isAxisEnabledFlags[a]},onAnimationEnd:function(){this.snapToBoundary();this.onScrollEnd()},onAnimationFrame:function(c,b,d){var a=this.position;a.x=this.convertX(-b);a.y=-d;this.onScroll()},onAxisDrag:function(d,q){if(q&&this.isAxisEnabled(d)){var r=this,l=r.flickStartPosition,s=r.flickStartTime,j=r.lastDragPosition,n=r.dragDirection,a=r.position[d],o=r.getMinUserPosition()[d],p=r.getMaxUserPosition()[d],h=r.startPosition[d],k=j[d],m=h-q,i=n[d],g=r.getOutOfBoundRestrictFactor(),b=r.getStartMomentumResetTime(),c=Ext.Date.now(),e;if(mp){e=m-p;m=p+e*g}}if(m>k){n[d]=1}else{if(mb){l[d]=a;s[d]=c}j[d]=m;return true}},onDomScroll:function(){var b=this,c,a;if(b.getTranslatable().isScrollParent){c=b.getElement().dom;a=b.position;a.x=c.scrollLeft;a.y=c.scrollTop}Ext.scroll.Scroller.prototype.onDomScroll.call(this)},onDrag:function(c){var a=this,b=a.lastDragPosition;if(!a.isDragging){return}if(a.onAxisDrag("x",a.convertX(c.deltaX))|a.onAxisDrag("y",c.deltaY)){a.doScrollTo(b.x,b.y)}},onDragEnd:function(d){var c=this,b,a;if(!c.isDragging){return}c.dragEndTime=Ext.Date.now();c.onDrag(d);c.isDragging=false;b=c.getAnimationEasing("x",d);a=c.getAnimationEasing("y",d);if(b||a){c.getTranslatable().animate(b,a)}else{c.onScrollEnd()}},onDragStart:function(m){var n=this,q=n.getDirection(),h=m.absDeltaX,g=m.absDeltaY,l=n.getDirectionLock(),j=n.startPosition,d=n.flickStartPosition,k=n.flickStartTime,i=n.lastDragPosition,c=n.position,b=n.dragDirection,p=c.x,o=c.y,a=Ext.Date.now();if(l&&q!=="both"){if((q==="horizontal"&&h>g)||(q==="vertical"&&g>h)){m.stopPropagation()}else{return}}i.x=p;i.y=o;d.x=p;d.y=o;j.x=p;j.y=o;k.x=a;k.y=a;b.x=0;b.y=0;n.dragStartTime=a;n.isDragging=true;if(!n.isScrolling){n.onScrollStart()}},onElementResize:function(a,b){this.refresh(true,{elementSize:{x:b.contentWidth,y:b.contentHeight},size:this.getAutoRefresh()?null:this.getSize()})},onElementScroll:function(a,b){b.scrollTop=b.scrollLeft=0},onEvent:function(b){var a=this,c=b.browserEvent;if((!a.self.isTouching||a.isTouching)&&((!a.getTranslatable().isScrollParent)||(!a.isMouseEvent[c.type]&&c.pointerType!=="mouse"))&&(a.getY()||a.getX())){a[a.listenerMap[b.type]](b)}},onInnerElementResize:function(a,b){this.refresh(true,{size:{x:b.width,y:b.height}})},onMouseWheel:function(k){var l=this,n=k.getWheelDeltas(),d=-n.x,b=-n.y,h=l.position,g=l.getMaxUserPosition(),a=l.getMinUserPosition(),m=Math.max,c=Math.min,j=m(c(h.x+d,g.x),a.x),i=m(c(h.y+b,g.y),a.y);d=j-h.x;b=i-h.y;if(!d&&!b){return}k.stopEvent();l.onScrollStart();l.scrollBy(d,b);l.onScroll();l.onScrollEnd()},onPartnerScrollEnd:function(a,c){var b=this;if(!b.getTranslatable().isScrollParent){b.fireScrollEnd(a,c)}Ext.scroll.Scroller.prototype.onPartnerScrollEnd.call(this,a,c);b.isScrolling=false;b.hideIndicators()},onPartnerScrollStart:function(a,c){var b=this;b.isScrolling=true;if(!b.getTranslatable().isScrollParent){b.fireScrollStart(a,c)}b.showIndicators()},onScroll:function(){var e=this,c=e.position,b=c.x,h=c.y,g=e.getIndicators(),a,d;if(g){if(e.isAxisEnabled("x")){a=g.x;if(a){a.setValue(b)}}if(e.isAxisEnabled("y")){d=g.y;if(d){d.setValue(h)}}}e.fireScroll(b,h)},onScrollEnd:function(){var b=this,a=b.position;if(b.isScrolling&&!b.isTouching&&!b.snapToSlot()){b.hideIndicators();b.isScrolling=Ext.isScrolling=false;b.fireScrollEnd(a.x,a.y)}},onScrollStart:function(){var b=this,a=b.position;if(!b.isScrolling){b.showIndicators();b.isScrolling=Ext.isScrolling=true;b.fireScrollStart(a.x,a.y)}},onTouchEnd:function(){var a=this;a.isTouching=a.self.isTouching=false;if(!a.isDragging&&a.snapToSlot()){a.onScrollStart()}},onTouchMove:function(a){a.preventDefault()},onTouchStart:function(){var a=this;a.isTouching=a.self.isTouching=true;Ext.getDoc().on({touchend:"onTouchEnd",scope:a,single:true});a.stopAnimation()},refreshAxes:function(){var e=this,c=e.isAxisEnabledFlags,l=e.getSize(),k=e.getElementSize(),j=e.getIndicators(),b,a,i,h,d,g;if(!l||!k){return}b=Math.max(0,l.x-k.x);a=Math.max(0,l.y-k.y);i=e.getX();h=e.getY();e.setMaxPosition({x:b,y:a});if(i===true||i==="auto"){c.x=!!b}else{if(i===false){c.x=false;d=j&&j.x;if(d){d.hide()}}else{if(i==="scroll"){c.x=true}}}if(h===true||h==="auto"){c.y=!!a}else{if(h===false){c.y=false;g=j&&j.y;if(g){g.hide()}}else{if(h==="scroll"){c.y=true}}}e.setMaxUserPosition({x:c.x?b:0,y:c.y?a:0});if(Ext.supports.touchScroll===1){e.initXStyle();e.initYStyle()}},showIndicators:function(){var c=this,d=c.getIndicators(),a,b;if(d){if(c.isAxisEnabled("x")){a=d.x;if(a){a.show()}}if(c.isAxisEnabled("y")){b=d.y;if(b){b.show()}}}},snapToBoundary:function(){var i=this,h=i.getPosition();if(i.isConfiguring||!(h.x||h.y)){return}var c=i.getMinUserPosition(),g=i.getMaxUserPosition(),e=c.x,d=c.y,b=g.x,a=g.y,k=Math.round(h.x),j=Math.round(h.y);if(kb){k=b}}if(ja){j=a}}i.doScrollTo(k,j)},snapToSlot:function(){var a=this,c=a.getSnapPosition("x"),b=a.getSnapPosition("y"),d=a.getSlotSnapEasing();if(c!==null||b!==null){a.doScrollTo(c,b,{easingX:d.x,easingY:d.y});return true}return false},stopAnimation:function(){this.getTranslatable().stopAnimation()},toggleResizeListeners:function(a){var c=this,b=c.getElement(),g,e,d;if(b){d=c.getInnerElement();if(a){g=e="on"}else{if(a===null){g="on";e="un"}else{g=e="un"}}b[g]("resize","onElementResize",c);d[e]("resize","onInnerElementResize",c)}},unwrapContent:function(){var a=this.getInnerElement().dom,b=this.getElement().dom,c;while((c=a.firstChild)){b.insertBefore(c,a)}},wrapContent:function(a){var b=document.createElement("div"),c=a.dom,d;while(d=c.lastChild){b.insertBefore(d,b.firstChild)}c.appendChild(b);this.setInnerElement(b);this._isWrapped=true;return this.getInnerElement()}}},1,0,0,0,["scroller.touch"],0,[Ext.scroll,"TouchScroller"],0));(Ext.cmd.derive("Ext.scroll.DomScroller",Ext.scroll.Scroller,{isDomScroller:true,getMaxPosition:function(){var b=this.getElement(),a=0,d=0,c;if(b&&!b.destroyed){c=b.dom;a=c.scrollWidth-c.clientWidth;d=c.scrollHeight-c.clientHeight}return{x:a,y:d}},getMaxUserPosition:function(){var c=this,b=c.getElement(),a=0,e=0,d;if(b&&!b.destroyed){d=b.dom;if(c.getX()){a=d.scrollWidth-d.clientWidth}if(c.getY()){e=d.scrollHeight-d.clientHeight}}return{x:a,y:e}},getPosition:function(){var a=this;if(a.positionDirty){a.updateDomScrollPosition()}return a.position},getSize:function(){var b=this.getElement(),a,c;if(b&&!b.destroyed){c=b.dom;a={x:c.scrollWidth,y:c.scrollHeight}}else{a={x:0,y:0}}return a},updateElement:function(b,a){this.initXStyle();this.initYStyle()},updateX:function(a){this.initXStyle()},updateY:function(a){this.initYStyle()},privates:{doScrollTo:function(k,j,a){var h=this,d=h.getElement(),e,b,l,m,g,c;if(d&&!d.destroyed){b=d.dom;m=(k===Infinity);g=(j===Infinity);if(m||g){e=h.getMaxPosition();if(m){k=e.x}if(g){j=e.y}}k=h.convertX(k);if(a){l={};if(j!=null){l.scrollTop=j}if(k!=null){l.scrollLeft=k}d.animate(Ext.mergeIf({to:{scrollTop:j,scrollLeft:k}},a))}else{if(j!=null){b.scrollTop=j}if(k!=null){b.scrollLeft=k;if(Ext.isIE8){c=b.scrollLeft;b.scrollLeft=k}}}h.positionDirty=true}},getElementScroll:function(a){return a.getScroll()},stopAnimation:function(){var a=this.getElement().getActiveAnimation();if(a){a.end()}}}},0,0,0,0,["scroller.dom"],0,[Ext.scroll,"DomScroller"],function(a){Ext.onDocumentReady(function(){a.document=new a({x:true,y:true,element:document.body})})}));(Ext.cmd.derive("Ext.util.Floating",Ext.Base,{mixinId:"floating",focusOnToFront:true,shadow:"sides",animateShadow:false,constrain:false,config:{activeCounter:0,alwaysOnTop:false},preventDefaultAlign:false,_visModeMap:{visibility:1,display:2,offsets:3},constructor:function(){var d=this,c=d.el,e=d.shadow,a,b;if(e){b={mode:(e===true)?"sides":e};a=d.shadowOffset;if(a){b.offset=a}b.animate=d.animateShadow;b.fixed=d.fixed;c.enableShadow(b,false)}if(d.shim||Ext.useShims){c.enableShim({fixed:d.fixed},false)}c.setVisibilityMode(d._visModeMap[d.hideMode]);d.el.on({mousedown:d.onMouseDown,scope:d,capture:true});d.registerWithOwnerCt();d.initHierarchyEvents()},alignTo:function(c,a,g,d){var e=this,b;Ext.un("scroll",e.onAlignToScroll,e);if(c.isComponent){b=c.el}else{if(c.nodeType){b=Ext.fly(c)}else{b=c}}if(!b.contains(e.el)){Ext.on("scroll",e.onAlignToScroll,e)}e._lastAlignTarget=c;e._lastAlignToPos=a;e.mixins.positionable.alignTo.call(e,b,a,g,d)},initFloatConstrain:function(){var a=this,b=a.floatParent;if((a.constrain||a.constrainHeader)&&!a.constrainTo){a.constrainTo=b?b.getTargetEl():a.container}},initHierarchyEvents:function(){var b=this,a=this.syncHidden;if(!b.hasHierarchyEventListeners){b.mon(Ext.GlobalEvents,{hide:a,collapse:a,show:a,expand:a,added:a,scope:b});b.hasHierarchyEventListeners=true}},registerWithOwnerCt:function(){var c=this,b=c.ownerCt,a=c.zIndexParent;if(a){a.unregisterFloatingItem(c)}a=c.zIndexParent=c.up("[floating]");c.floatParent=b||a;c.initFloatConstrain();delete c.ownerCt;if(a){a.registerFloatingItem(c)}else{Ext.WindowManager.register(c)}},onMouseDown:function(g){var b=this,a=b.focusTask,c=g.pointerType==="touch",d,h,i;if(b.floating&&(!a||!a.id)){d=g.target;h=b.el.dom;while(!c&&d&&d!==h){if(Ext.fly(d).isFocusable()){c=true}d=d.parentNode}i=Ext.WindowManager.getActive()===b&&(d===h||c);if(!i){b.toFront(c)}}},onBeforeFloatLayout:function(){this.el.preventSync=true},onAfterFloatLayout:function(){var a=this.el;if(a.shadow||a.shim){a.setUnderlaysVisible(true);a.syncUnderlays()}},syncHidden:function(){var c=this,d=c.hidden||!c.rendered,a=c.hierarchicallyHidden=c.isHierarchicallyHidden(),b=c.pendingShow;if(d!==a){if(a){c.hide();c.pendingShow=true}else{if(b){delete c.pendingShow;if(b.length){c.show.apply(c,b)}else{c.show()}}}}},setZIndex:function(a){var b=this;b.el.setZIndex(a);a+=10;if(b.floatingDescendants){a=Math.floor(b.floatingDescendants.setBase(a)/100)*100+10000}return a},doConstrain:function(a){var b=this,c=b.calculateConstrainedPosition(a,null,true);if(c){b.setPosition(c)}},updateActiveCounter:function(a){var b=this.zIndexParent;if(b&&this.bringParentToFront!==false){b.setActiveCounter(++Ext.ZIndexManager.activeCounter)}b=this.zIndexManager;if(b){b.onComponentUpdate(this)}},updateAlwaysOnTop:function(a){var b=this.zIndexManager;if(b){b.onComponentUpdate(this)}},toFront:function(b){var a=this;if(a.zIndexManager.bringToFront(a,b||!a.focusOnToFront)){if(a.hasListeners.tofront){a.fireEvent("tofront",a,a.el.getZIndex())}}return a},setActive:function(d,b){var c=this,a;if(d){if(c.el.shadow&&!c.maximized){c.el.enableShadow(null,true)}if(b){a=Ext.ComponentManager.getActiveComponent();if(!a||!a.up(c)){c.focus()}}c.fireEvent("activate",c)}else{c.fireEvent("deactivate",c)}},toBack:function(){this.zIndexManager.sendToBack(this);return this},center:function(){var a=this,b;if(a.isVisible()){b=a.getAlignToXY(a.container,"c-c");a.setPagePosition(b)}else{a.needsCenter=true}return a},onFloatShow:function(){if(this.needsCenter){this.center()}delete this.needsCenter;if(this.toFrontOnShow){this.toFront()}},fitContainer:function(c){var g=this,e=g.floatParent,b=e?e.getTargetEl():g.container,a=b.getViewSize(),d=e||(b.dom!==document.body)?[0,0]:b.getXY();a.x=d[0];a.y=d[1];g.setBox(a,c)},privates:{onFloatDestroy:function(){this.clearAlignEl()},clearAlignEl:function(){var a=this;if(a._lastAlignTarget){Ext.un("scroll",a.onAlignToScroll,a);a._lastAlignPos=a._lastAlignTarget=null}},onAlignToScroll:function(a){var e=this,b=e._lastAlignTarget,c=b.isFloating,d,g;if(b){if(b.isComponent){d=b.destroyed;b=b.el}else{if(b.nodeType){b=Ext.fly(b)}g=b.dom;d=!g||Ext.isGarbage(g)}if(d){e.clearAlignEl()}else{if((a.getElement().contains(b)||c)&&!a.getElement().contains(e.el)){e.alignTo(b,e._lastAlignToPos)}}}}}},1,0,0,0,0,0,[Ext.util,"Floating"],0));(Ext.cmd.derive("Ext.util.ElementContainer",Ext.Base,{mixinId:"elementCt",config:{childEls:{$value:{},cached:true,lazy:true,merge:function(g,a,e,d){var c=a?Ext.Object.chain(a):{},b,h;if(g instanceof Array){for(b=g.length;b--;){h=g[b];if(!d||!(h in c)){if(typeof h==="string"){c[h]={name:h,itemId:h}}else{c[h.name]=h}}}}else{if(g){if(g.constructor===Object){for(b in g){if(!d||!(b in c)){h=g[b];if(h===true){c[b]={itemId:b}}else{if(typeof h==="string"){c[b]={itemId:h}}else{c[b]=h;if(!("itemId" in h)){h.itemId=b}}}c[b].name=b}}}else{if(!d||!(g in c)){c[g]={name:g,itemId:g}}}}}return c}}},destroy:function(){var c=this,b=c.getChildEls(),d,a;for(a in b){d=c[a];if(d){if(d.destroy){d.component=null;d.destroy()}c[a]=null}}},privates:{addChildEl:function(a){var c=this,b=c.getChildEls();if(!c.hasOwnProperty("childEls")){c.childEls=b=Ext.Object.chain(b)}if(typeof a==="string"){a={name:a,itemId:a}}b[a.name]=a},afterClassMixedIn:function(c){var b=c.prototype,a=b.childEls;if(a){delete b.childEls;c.getConfigurator().add({childEls:a})}},attachChildEls:function(d,c){var i=this,j=i.getChildEls(),h=c||i,o=h.id+"-",n=!h.frame,p,a,m,g,e,l,b;for(p in j){m=j[p];if(n&&m.frame){continue}e=m.select;if(e){l=d.select(e,true)}else{if(!(e=m.selectNode)){if(!(b=m.id)){b=o+m.itemId;l=Ext.cache[b]}else{l=Ext.cache[b]||d.getById(b)}}else{l=d.selectNode(e,false)}}if(l){if(l.isElement){l.component=h}else{if(l.isComposite&&!l.isLite){a=l.elements;for(g=a.length;g--;){a[g].component=h}}}}i[p]=l||null}}}},0,0,0,0,0,0,[Ext.util,"ElementContainer"],0));(Ext.cmd.derive("Ext.util.Renderable",Ext.Base,{mixinId:"renderable",frameCls:"x-frame",frameIdRegex:/[\-]frame\d+[TMB][LCR]$/,frameElNames:["TL","TC","TR","ML","MC","MR","BL","BC","BR","Table"],frameTpl:["{%this.renderDockedItems(out,values,0);%}",'','
    {parent.baseCls}-{parent.ui}-{.}-tl{frameElCls}" role="presentation">','
    {parent.baseCls}-{parent.ui}-{.}-tr{frameElCls}" role="presentation">','
    {parent.baseCls}-{parent.ui}-{.}-tc{frameElCls}" role="presentation">
    ','
    ','
    ',"
    ",'
    {parent.baseCls}-{parent.ui}-{.}-ml{frameElCls}" role="presentation">','
    {parent.baseCls}-{parent.ui}-{.}-mr{frameElCls}" role="presentation">','
    {parent.baseCls}-{parent.ui}-{.}-mc{frameElCls}" role="presentation">',"{%this.applyRenderTpl(out, values)%}","
    ",'
    ','
    ','','
    {parent.baseCls}-{parent.ui}-{.}-bl{frameElCls}" role="presentation">','
    {parent.baseCls}-{parent.ui}-{.}-br{frameElCls}" role="presentation">','
    {parent.baseCls}-{parent.ui}-{.}-bc{frameElCls}" role="presentation">
    ','
    ','
    ',"
    ","{%this.renderDockedItems(out,values,1);%}"],frameTableTpl:["{%this.renderDockedItems(out,values,0);%}",'','','','','','',"","",'','','",'',"",'','','','','',"","","","{%this.renderDockedItems(out,values,1);%}"],_renderState:0,ariaEl:"el",_layerCls:"x-layer",_fixedLayerCls:"x-fixed-layer",ariaStaticRoles:{presentation:true,article:true,definition:true,directory:true,document:true,img:true,heading:true,math:true,note:true,banner:true,complementary:true,contentinfo:true,navigation:true,search:true,"undefined":true,"null":true},statics:{makeRenderSetter:function(a,c){var b=a.name;return function(e){var d=this,h=(d.renderConfigs||(d.renderConfigs={})),g=h[c];if(d._renderState>=c){(a.setter||a.getSetter()).call(d,e)}else{if(!g){h[c]=g={}}if(!(b in g)){g[b]=d[b]}d[b]=e}return d}},processRenderConfig:function(a,l,c){var h=this.prototype,e=this.getConfigurator(),m=Ext.util.Renderable,k=m.makeRenderSetter,d=a[l],i,j,b,g;for(b in d){j=Ext.Config.get(b);if(!h[g=j.names.set]){i=(j.renderSetter||(j.renderSetter={}));h[g]=i[c]||(i[c]=k(j,c))}}delete a[l];e.add(d)}},onClassMixedIn:function(c){var a=c.override,e=this.processRenderConfig,d=function(g){if(g.beforeRenderConfig){this.processRenderConfig(g,"beforeRenderConfig",1)}if(g.renderConfig){this.processRenderConfig(g,"renderConfig",3)}a.call(this,g)},b=function(h,g){h.override=d;h.processRenderConfig=e;if(g.beforeRenderConfig){h.processRenderConfig(g,"beforeRenderConfig",1)}if(g.renderConfig){h.processRenderConfig(g,"renderConfig",3)}};b(c,c.prototype);c.onExtended(b)},afterRender:function(){var i=this,d={},b=i.protoEl,h=i.el,e,j,c,g,a;i.finishRenderChildren();i._renderState=4;if(i.contentEl){c="x-";g=c+"hidden-";a=i.contentEl=Ext.get(i.contentEl);a.component=i;a.removeCls([c+"hidden",g+"display",g+"offsets"]);i.getContentTarget().appendChild(a.dom)}b.writeTo(d);j=d.removed;if(j){h.removeCls(j)}j=d.cls;if(j.length){h.addCls(j)}j=d.style;if(d.style){h.setStyle(j)}i.protoEl=null;if(!i.ownerCt&&!i.skipLayout){i.updateLayout()}if(!(i.x&&i.y)&&(i.pageX||i.pageY)){i.setPagePosition(i.pageX,i.pageY)}if(i.disableOnRender){i.onDisable()}e=i.controller;if(e&&e.afterRender){e.afterRender(i)}},afterFirstLayout:function(d,l){var g=this,k=g.x,i=g.y,j=g.defaultAlign,b=g.alignOffset,e,c,a,h,m;if(!g.ownerLayout){c=k!==undefined;a=i!==undefined}if(g.floating&&!g.preventDefaultAlign&&(!c||!a)){if(g.floatParent){h=g.floatParent.getTargetEl().getViewRegion();m=g.el.getAlignToXY(g.alignTarget||g.floatParent.getTargetEl(),j,b);h.x=m[0]-h.x;h.y=m[1]-h.y}else{m=g.el.getAlignToXY(g.alignTarget||g.container,j,b);h=g.el.translateXY(m[0],m[1])}k=c?k:h.x;i=a?i:h.y;c=a=true}if(c||a){g.setPosition(k,i)}g.onBoxReady(d,l);e=g.controller;if(e&&e.boxReady){e.boxReady(g)}},beforeRender:function(){var d=this,e=d.floating,c=d.getComponentLayout(),b=d.userCls,a;d._renderState=1;d.ariaUsesMainElement=d.ariaEl==="el";a=d.controller;if(a&&a.beforeRender){a.beforeRender(d)}d.initBindable();if(d.renderConfigs){d.flushRenderConfigs()}if(d.reference){d.publishState()}if(b){d.addCls(b)}if(e){d.addCls(d.fixed?d._fixedLayerCls:d._layerCls);b=e.cls;if(b){d.addCls(b)}}d.frame=d.frame||d.alwaysFramed;if(!c.initialized){c.initLayout()}d.initOverflow();d.setUI(d.ui)},doApplyRenderTpl:function(c,a){var d=a.$comp,b;if(!d.rendered){b=d.initRenderTpl();b.applyOut(a.renderData,c)}},getElConfig:function(){var e=this,h=e.autoEl,d=e.getFrameInfo(),b={tag:"div",tpl:d?e.initFramingTpl(d.table):e.initRenderTpl()},i=e.layoutTargetCls,g=e.protoEl,a=e.ariaRole,c;e.initStyles(g);if(i&&!d){g.addCls(i)}g.writeTo(b);g.flush();if(h){if(Ext.isString(h)){b.tag=h}else{Ext.apply(b,h)}}if(a&&e.ariaUsesMainElement){b.role=a;if(!e.ariaStaticRoles[a]){b["aria-hidden"]=!!e.hidden;b["aria-disabled"]=!!e.disabled;if(e.ariaLabel&&!e.ariaLabelledBy){b["aria-label"]=e.ariaLabel}if(e.collapsible){b["aria-expanded"]=!e.collapsed}if(e.ariaRenderAttributes){Ext.apply(b,e.ariaRenderAttributes)}if(e.config.ariaAttributes){Ext.apply(b,e.getAriaAttributes())}}}b.id=e.id;if(b.tpl){if(d){b.tplData=c=e.getFrameRenderData();c.renderData=e.initRenderData()}else{b.tplData=e.initRenderData()}}e.ariaRenderAttributes=null;return b},getInsertPosition:function(a){if(a!==undefined){if(Ext.isNumber(a)){a=this.container.dom.childNodes[a]}else{a=Ext.getDom(a)}}return a},getRenderTree:function(){var b=this,a=null;if(!b.hasListeners.beforerender||b.fireEvent("beforerender",b)!==false){b._renderState=1;b.beforeRender();b.rendering=true;b._renderState=2;a=b.getElConfig();if(b.el){a.id=b.$pid=Ext.id(null,b.el.identifiablePrefix)}}return a},initRenderData:function(){var c=this,a=c.ariaRole,d,b;d=Ext.apply({$comp:c,id:c.id,ui:c.ui,uiCls:c.uiCls,baseCls:c.baseCls,componentCls:c.componentCls,frame:c.frame,renderScroller:c.touchScroll,scrollerCls:c.scrollerCls,childElCls:""},c.renderData);if(a&&!c.ariaUsesMainElement){b={role:a};if(!c.ariaStaticRoles[a]){b["aria-hidden"]=!!c.hidden;b["aria-disabled"]=!!c.disabled;if(c.ariaLabel&&!c.ariaLabelledBy){b["aria-label"]=c.ariaLabel}if(c.collapsible){b["aria-expanded"]=!c.collapsed}if(c.ariaRenderAttributes){Ext.apply(b,c.ariaRenderAttributes)}if(c.config.ariaAttributes){Ext.apply(b,c.getAriaAttributes())}}d.ariaAttributes=b}return d},onRender:function(d,e){var g=this,i=g.x,h=g.y,c=null,b=g.el,a,j;g.applyRenderSelectors();g.rendering=null;g.rendered=true;g._renderState=3;if(g.renderConfigs){g.flushRenderConfigs()}if(i!=null){c={x:i}}if(h!=null){(c=c||{}).y=h}if(!g.getFrameInfo()){a=g.width;j=g.height;if(typeof a==="number"){c=c||{};c.width=a}if(typeof j==="number"){c=c||{};c.height=j}}if(g.touchScroll===1){g.getOverflowEl().disableTouchScroll()}g.lastBox=b.lastBox=c},render:function(c,b){var e=this,d=e.el,i=e.ownerLayout,h,a,g;if(d&&!d.isElement){e.wrapPrimaryEl(d);d=e.el}if(!e.skipLayout){Ext.suspendLayouts()}c=e.initContainer(c);g=e.getInsertPosition(b);if(!d){a=e.getRenderTree();if(i&&i.transformItemRenderTree){a=i.transformItemRenderTree(a)}if(a){if(g){d=Ext.DomHelper.insertBefore(g,a)}else{d=Ext.DomHelper.append(c,a)}e.wrapPrimaryEl(d);e.cacheRefEls(d)}}else{if(!e.hasListeners.beforerender||e.fireEvent("beforerender",e)!==false){e.beforeRender();e.needsRenderTpl=e.rendering=true;e._renderState=2;e.initStyles(d);if(e.allowDomMove!==false){if(g){c.dom.insertBefore(d.dom,g)}else{c.dom.appendChild(d.dom)}}}else{h=true}}if(d&&!h){e.finishRender(b)}if(!e.skipLayout){Ext.resumeLayouts(!e.hidden&&!c.isDetachedBody)}},ensureAttachedToBody:function(c){var b=this,a;while(b.ownerCt){b=b.ownerCt}if(b.container.isDetachedBody){b.container=a=Ext.getBody();a.appendChild(b.el.dom);if(c){b.updateLayout()}if(typeof b.x==="number"||typeof b.y==="number"){b.setPosition(b.x,b.y)}}},privates:{applyRenderSelectors:function(){var d=this,b=d.renderSelectors,c=d.el,e,a;d.attachChildEls(c);d.ariaEl=d[d.ariaEl]||d.el;if(b){for(a in b){e=b[a];if(e){d[a]=c.selectNode(e,false)}}}},cacheRefEls:function(e){e=e||this.el;var c=Ext.cache,h=Ext.dom.Element,j=e.isElement?e.dom:e,b=j.querySelectorAll("[data-ref]"),a=b.length,g,d;for(d=0;d','
    ',"","{%this.renderContent(out,values)%}",'
    '],resizeHandles:"all",shrinkWrap:2,toFrontOnShow:true,synthetic:false,tplWriteMode:"overwrite",ui:"default",uiCls:[],userCls:null,weight:null,allowDomMove:true,autoGenId:false,borderBoxCls:"x-border-box",componentLayoutCounter:0,contentPaddingProperty:"padding",deferLayouts:false,frameSize:null,horizontalPosProp:"left",isComponent:true,_isLayoutRoot:false,layoutSuspendCount:0,liquidLayout:false,maskOnDisable:true,offsetsCls:"x-hidden-offsets",rendered:false,rootCls:"x-body",scrollerCls:"x-scroll-scroller",scrollerSelector:".x-scroll-scroller",_scrollFlags:{auto:{auto:{overflowX:"auto",overflowY:"auto",x:true,y:true,both:true},"false":{overflowX:"auto",overflowY:"hidden",x:true,y:false,both:false},scroll:{overflowX:"auto",overflowY:"scroll",x:true,y:true,both:true}},"false":{auto:{overflowX:"hidden",overflowY:"auto",x:false,y:true,both:false},"false":{overflowX:"hidden",overflowY:"hidden",x:false,y:false,both:false},scroll:{overflowX:"hidden",overflowY:"scroll",x:false,y:true,both:false}},scroll:{auto:{overflowX:"scroll",overflowY:"auto",x:true,y:true,both:true},"false":{overflowX:"scroll",overflowY:"hidden",x:true,y:false,both:false},scroll:{overflowX:"scroll",overflowY:"scroll",x:true,y:true,both:true}},none:{overflowX:"",overflowY:"",x:false,y:false,both:false}},_scrollableCfg:{x:{x:true,y:false},y:{x:false,y:true},horizontal:{x:true,y:false},vertical:{x:false,y:true},both:{x:true,y:true},"true":{x:true,y:true}},validIdRe:Ext.validIdRe,constructor:function(a){var h=this,c,g,j,e,l,d,b,k;a=a||{};if(a.initialConfig){if(a.isAction){h.baseAction=a}a=a.initialConfig}else{if(a.tagName||a.dom||Ext.isString(a)){a={applyTo:a,id:a.id||a}}}h.initialConfig=a;h.getId();h.protoEl=new Ext.util.ProtoElement();h.initConfig(a);if(h.scrollable==null){l=h.autoScroll;if(l){k=!!l}else{d=h.overflowX;b=h.overflowY;if(d||b){k={x:(d&&d!=="hidden")?d:false,y:(b&&b!=="hidden")?b:false}}}if(k){h.setScrollable(k)}}j=h.xhooks;if(j){delete h.xhooks;Ext.override(h,j)}h.mixins.elementCt.constructor.call(h);h.setupProtoEl();if(h.cls){h.initialCls=h.cls;h.protoEl.addCls(h.cls)}if(h.style){h.initialStyle=h.style;h.protoEl.setStyle(h.style)}h.renderData=h.renderData||{};h.initComponent();if(!h.preventRegister){Ext.ComponentManager.register(h)}h.mixins.state.constructor.call(h);h.addStateEvents("resize");e=h.getController();if(e){e.init(h)}if(h.plugins){for(c=0,g=h.plugins.length;ce){q=k;n=true}if(g&&a>p){m=a;n=true}if(l||g){j=t.el.getStyle("overflow");if(j!=="hidden"){t.el.setStyle("overflow","hidden")}}if(n){b=!Ext.isNumber(t.width);s=!Ext.isNumber(t.height);t.setSize(m,q);t.el.setSize(p,e);if(b){delete t.width}if(s){delete t.height}}if(g){d.width=a}if(l){d.height=k}}i=t.constrain;o=t.constrainHeader;if(i||o){t.constrain=t.constrainHeader=false;r=c.callback;c.callback=function(){t.constrain=i;t.constrainHeader=o;if(r){r.call(c.scope||t,arguments)}if(j!=="hidden"){t.el.setStyle("overflow",j)}}}return t.mixins.animate.animate.apply(t,arguments)},applyScrollable:function(b,d){var a=this,g=a.rendered,e,c;if(b){if(b===true||typeof b==="string"){e=a._scrollableCfg[b];b=e}if(d){d.setConfig(b);b=d}else{b=Ext.Object.chain(b);if(g){b.element=a.getOverflowEl();c=a.getScrollerEl();if(c){b.innerElement=c}}b.autoRefresh=false;if(Ext.supports.touchScroll===1){b.translatable={translationMethod:"scrollparent"};b.indicators=false}b=Ext.scroll.Scroller.create(b);b.component=a}}else{if(d){d.setConfig({x:false,y:false});d.destroy();b=null}}if(a.rendered&&!a.destroying&&!a.destroyed){if(b){a.getOverflowStyle()}else{a.scrollFlags=a._scrollFlags.none}a.updateLayout()}return b},beforeComponentLayout:function(){return true},beforeDestroy:Ext.emptyFn,beforeLayout:function(){if(this.floating){this.onBeforeFloatLayout()}},beforeSetPosition:function(j,h,b){var g=this,i=null,d,c,a,e;if(j){if(Ext.isNumber(d=j[0])){b=h;h=j[1];j=d}else{if((d=j.x)!==undefined){b=h;h=j.y;j=d}}}if(g.constrain||g.constrainHeader){i=g.calculateConstrainedPosition(null,[j,h],true);if(i){j=i[0];h=i[1]}}c=(j!==undefined);a=(h!==undefined);if(c||a){g.x=j;g.y=h;e=g.adjustPosition(j,h);i={x:e.x,y:e.y,anim:b,hasX:c,hasY:a}}return i},beforeShow:Ext.emptyFn,bubble:function(c,b,a){var d=this;while(d){if(c.apply(b||d,a||[d])===false){break}d=d.getBubbleTarget()}return this},clearListeners:function(){var a=this;a.mixins.observable.clearListeners.call(a);a.mixins.componentDelegation.clearDelegatedListeners.call(a)},cloneConfig:function(c){c=c||{};var d=c.id||Ext.id(),a=Ext.applyIf(c,this.initialConfig),b;a.id=d;b=Ext.getClass(this);return new b(a)},destroy:function(){var g=this,c=g.renderSelectors,b=g.getConfig("viewModel",true),h=g.getConfig("session",true),a,e,d;if(!g.hasListeners.beforedestroy||g.fireEvent("beforedestroy",g)!==false){g.isDestroying=g.destroying=true;e=g.floatParent||g.ownerCt;if(g.floating){delete g.floatParent;if(g.zIndexManager){g.zIndexManager.unregister(g);g.zIndexManager=null}}g.removeBindings();g.beforeDestroy();g.destroyBindable();if(e&&e.remove){e.remove(g,false)}g.stopAnimation();g.onDestroy();Ext.destroy(g.plugins);if(g.rendered){Ext.Component.cancelLayout(g,true)}g.componentLayout=null;if(g.hasListeners.destroy){g.fireEvent("destroy",g)}if(!g.preventRegister){Ext.ComponentManager.unregister(g)}g.mixins.state.destroy.call(g);if(g.floating){g.onFloatDestroy()}g.clearListeners();if(g.rendered){if(!g.preserveElOnDestroy){g.el.destroy()}g.el.component=null;g.mixins.elementCt.destroy.call(g);if(c){for(a in c){if(c.hasOwnProperty(a)){d=g[a];if(d){delete g[a];d.destroy()}}}}g.data=g.el=g.frameBody=g.rendered=g.afterRenderEvents=null;g.tpl=g.renderTpl=g.renderData=null;g.focusableContainer=g.container=g.scrollable=null}g.isDestroying=g.destroying=false;g.callParent()}},disable:function(c,e){var d=this,b=d.focusableContainer,a=d.getInherited();if(!e){a.disabled=true;d.savedDisabled=true}if(d.maskOnDisable){a.disableMask=true}if(!d.disabled){if(b){b.beforeFocusableChildDisable(d)}d.addCls(d.disabledCls);if(d.rendered){d.onDisable()}else{d.disableOnRender=true}d.disabled=true;if(c!==true){d.fireEvent("disable",d)}if(b){b.onFocusableChildDisable(d)}}return d},doFireEvent:function(b,d,a){var e=this,c=e.mixins.observable.doFireEvent.call(e,b,d,a);if(c!==false){c=e.mixins.componentDelegation.doFireDelegatedEvent.call(e,b,d)}return c},enable:function(c,e){var d=this,b=d.focusableContainer,a=d.getInherited();if(!e){delete d.getInherited().disabled;d.savedDisabled=false}if(d.maskOnDisable){delete a.disableMask}if(d.disabled){if(!(e&&a.hasOwnProperty("disabled"))){if(b){b.beforeFocusableChildEnable(d)}d.disableOnRender=false;d.removeCls(d.disabledCls);if(d.rendered){d.onEnable()}d.disabled=false;if(c!==true){d.fireEvent("enable",d)}if(b){b.onFocusableChildEnable(d)}}}return d},findParentBy:function(a){var b;for(b=this.getRefOwner();b&&!a(b,this);b=b.getRefOwner()){}return b||null},findParentByType:function(a){return Ext.isFunction(a)?this.findParentBy(function(b){return b.constructor===a}):this.up(a)},findPlugin:function(d){var b,a=this.plugins,c=a&&a.length;for(b=0;b-1;e--){c=g[e];if(c.query){a=c.query(b);a=a[a.length-1];if(a){return a}}if(c.is(b)){return c}}return h.previousNode(b,true)}return null},previousSibling:function(b){var e=this.ownerCt,d,a,g;if(e){d=e.items;a=d.indexOf(this);if(a!==-1){if(b){for(--a;a>=0;a--){if((g=d.getAt(a)).is(b)){return g}}}else{if(a){return d.getAt(--a)}}}}return null},registerFloatingItem:function(b){var a=this;if(!a.floatingDescendants){a.floatingDescendants=new Ext.ZIndexManager(a)}a.floatingDescendants.register(b)},removeCls:function(a){var c=this,b=c.rendered?c.el:c.protoEl;b.removeCls.apply(b,arguments);return c},removeClsWithUI:function(d,l){var k=this,j=[],g=0,a=Ext.Array,h=a.remove,e=k.uiCls=a.clone(k.uiCls),c=k.activeUI,b,m;if(typeof d==="string"){d=(d.indexOf(" ")<0)?[d]:Ext.String.splitWords(d)}b=d.length;for(g=0;g1){arguments[0]=null;c.pendingShow=arguments}else{c.pendingShow=true}}else{if(e&&c.isVisible()){if(c.floating){c.onFloatShow()}}else{if(c.fireEvent("beforeshow",c)!==false){c.hidden=false;delete this.getInherited().hidden;Ext.suspendLayouts();if(!e&&(c.autoRender||c.floating)){c.doAutoRender();e=c.rendered}if(e){c.beforeShow();Ext.resumeLayouts();c.onShow.apply(c,arguments);c.afterShow.apply(c,arguments)}else{Ext.resumeLayouts(true)}}else{c.onShowVeto()}}}return c},showAt:function(a,d,b){var c=this;if(!c.rendered&&(c.autoRender||c.floating)){c.x=a;c.y=d;return c.show()}if(c.floating){c.setPosition(a,d,b)}else{c.setPagePosition(a,d,b)}return c.show()},showBy:function(b,d,c){var a=this;if(a.floating&&b){a.alignTarget=b;if(d){a.defaultAlign=d}if(c){a.alignOffset=c}a.show();if(!a.hidden){a.alignTo(b,d||a.defaultAlign,c||a.alignOffset)}}return a},suspendLayouts:function(){var a=this;if(!a.rendered){return}if(++a.layoutSuspendCount===1){a.suspendLayout=true}},unitizeBox:function(a){return Ext.Element.unitizeBox(a)},unmask:function(){(this.getMaskTarget()||this.el).unmask();this.setMasked(false)},unregisterFloatingItem:function(b){var a=this;if(a.floatingDescendants){a.floatingDescendants.unregister(b)}},up:function(d,e){var c=this.getRefOwner(),b=typeof e==="string",h=typeof e==="number",a=e&&e.isComponent,g=0;if(d){for(;c;c=c.getRefOwner()){g++;if(d.isComponent){if(c===d){return c}}else{if(Ext.ComponentQuery.is(c,d)){return c}}if(b&&c.is(e)){return}if(h&&g===e){return}if(a&&c===e){return}}}return c},update:function(d,h,k,b){var j=this,l=(j.tpl&&!Ext.isString(d)),i=j.getScrollable(),c=j.focusableContainer,g,a,e;if(l){j.data=(d&&d.isEntity)?d.getData(true):d}else{j.html=Ext.isObject(d)?Ext.DomHelper.markup(d):d}if(j.rendered){g=j.getSizeModel();a=g.width.shrinkWrap||g.height.shrinkWrap;if(j.isContainer){e=j.layout.getRenderTarget();a=a||j.items.items.length>0}else{e=j.touchScroll?j.getScrollerEl():j.getTargetEl()}if(l){j.tpl[j.tplWriteMode](e,j.data||{})}else{e.setHtml(j.html,h,k,b||j)}if(a){j.updateLayout()}if(i){i.refresh(true)}if(c){c.onFocusableChildUpdate(j)}}},setHtml:function(b,a,c){this.update(b,a,null,c)},applyData:function(a){this.update(a)},updateBox:function(a){this.setSize(a.width,a.height);this.setPagePosition(a.x,a.y);return this},_asLayoutRoot:{isRoot:true},_notAsLayoutRoot:{isRoot:false},updateLayout:function(c){var d=this,e,b=d.lastBox,a=c&&c.isRoot;if(b){b.invalid=true}if(!d.rendered||d.layoutSuspendCount||d.suspendLayout){return}if(d.hidden){Ext.Component.cancelLayout(d)}else{if(typeof a!=="boolean"){a=d.isLayoutRoot()}}if(a||!d.ownerLayout||!d.ownerLayout.onContentChange(d)){if(!d.isLayoutSuspended()){e=(c&&c.hasOwnProperty("defer"))?c.defer:d.deferLayouts;Ext.Component.updateLayout(d,e)}}},updateMaxHeight:function(b,a){this.changeConstraint(b,a,"min","max-height","height")},updateMaxWidth:function(b,a){this.changeConstraint(b,a,"min","max-width","width")},updateMinHeight:function(b,a){this.changeConstraint(b,a,"max","min-height","height")},updateMinWidth:function(a,b){this.changeConstraint(a,b,"max","min-width","width")},getAnchorToXY:function(d,a,c,b){return d.getAnchorXY(a,c,b)},getBorderPadding:function(){return this.el.getBorderPadding()},getLocalX:function(){return this.el.getLocalX()},getLocalXY:function(){return this.el.getLocalXY()},getLocalY:function(){return this.el.getLocalY()},getX:function(){return this.el.getX()},getXY:function(){return this.el.getXY()},getY:function(){return this.el.getY()},setLocalX:function(a){this.el.setLocalX(a)},setLocalXY:function(a,b){this.el.setLocalXY(a,b)},setLocalY:function(a){this.el.setLocalY(a)},setX:function(a,b){this.el.setX(a,b)},setXY:function(b,a){this.el.setXY(b,a)},setY:function(b,a){this.el.setY(b,a)},privates:{addOverCls:function(){var a=this;if(!a.disabled){a.el.addCls(a.overCls)}},addUIToElement:function(){var d=this,a=d.baseCls+"-"+d.ui,c,g,b,e;d.addCls(a);if(d.rendered&&d.frame&&!Ext.supports.CSS3BorderRadius){a+="-";c=d.getChildEls();for(g in c){e=c[g].frame;if(e&&e!==true){b=d[g];if(b){b.addCls(a+e)}}}}},changeConstraint:function(h,c,a,e,b){var g=this,d=g[b];if(h!=null&&typeof d==="number"){g[b]=Math[a](d,h)}if(g.liquidLayout){if(h!=null){g.setStyle(e,h+"px")}else{if(c){g.setStyle(e,"")}}}if(g.rendered){g.updateLayout()}},constructPlugin:function(b){var a=this;if(typeof b==="string"){b=Ext.PluginManager.create({},b,a)}else{b=Ext.PluginManager.create(b,null,a)}return b},constructPlugins:function(){var e=this,c=e.plugins,b,d,a;if(c){b=[];b.processed=true;if(!Ext.isArray(c)){c=[c]}for(d=0,a=c.length;d=0){m=n[k].splitterDelta;if(i.getAt(h+m)!==a){i.remove(a);h=i.indexOf(j);if(m>0){++h}i.insert(h,a)}}}if(l){if(e){j.expand(false)}b.remove(l);j.placeholder=null;if(e){j.collapse(null,false)}}b.updateLayout();Ext.resumeLayouts(true);j.fireEventArgs("changeregion",[j,d])}else{j.region=k}}return d},setWeight:function(d){var c=this,b=c.getOwningBorderContainer(),e=c.placeholder,a=c.weight;if(d!==a){if(c.fireEventArgs("beforechangeweight",[c,d])!==false){c.weight=d;if(e){e.weight=d}if(b){b.updateLayout()}c.fireEventArgs("changeweight",[c,a])}}return a}},function(a){var b=a.prototype;b.setBorderRegion=b.setRegion;b.setRegionWeight=b.setWeight});Ext.define("Ext.overrides.app.domain.Component",{override:"Ext.app.domain.Component"},function(a){a.monitor(Ext.Component)});(Ext.cmd.derive("Ext.app.EventBus",Ext.Base,{singleton:true,constructor:function(){var b=this,a=Ext.app.EventDomain.instances;b.callParent();b.domains=a;b.bus=a.component.bus},control:function(b,a){return this.domains.component.listen(b,a)},listen:function(d,b){var a=this.domains,c;for(c in d){if(d.hasOwnProperty(c)){a[c].listen(d[c],b)}}},unlisten:function(c){var a=Ext.app.EventDomain.instances,b;for(b in a){a[b].unlisten(c)}}},1,0,0,0,0,0,[Ext.app,"EventBus"],0));(Ext.cmd.derive("Ext.app.domain.Global",Ext.app.EventDomain,{singleton:true,type:"global",constructor:function(){var a=this;a.callParent();a.monitor(Ext.GlobalEvents)},listen:function(b,a){this.callParent([{global:b},a])},match:Ext.returnTrue},1,0,0,0,0,0,[Ext.app.domain,"Global"],0));(Ext.cmd.derive("Ext.app.BaseController",Ext.Base,{isController:true,config:{id:null,control:null,listen:null,routes:null,before:null},constructor:function(a){var b=this;Ext.apply(b,a);delete b.control;delete b.listen;b.eventbus=Ext.app.EventBus;b.mixins.observable.constructor.call(b,a);b.ensureId()},applyListen:function(a){if(Ext.isObject(a)){a=Ext.clone(a)}return a},applyControl:function(a){if(Ext.isObject(a)){a=Ext.clone(a)}return a},updateControl:function(a){this.ensureId();if(a){this.control(a)}},updateListen:function(a){this.ensureId();if(a){this.listen(a)}},updateRoutes:function(b){if(b){var e=this,g=e.getBefore()||{},a=Ext.app.route.Router,d,c,h;for(d in b){c=b[d];if(Ext.isString(c)){c={action:c}}h=c.action;if(!c.before){c.before=g[h]}a.connect(d,c,e)}}},isActive:function(){return true},control:function(b,c,a){var d=this,e=a,g;if(Ext.isString(b)){g={};g[b]=c}else{g=b;e=c}d.eventbus.control(g,e||d)},listen:function(b,a){this.eventbus.listen(b,a||this)},destroy:function(){var b=this,a=b.eventbus;Ext.app.route.Router.disconnectAll(b);if(a){a.unlisten(b);b.eventbus=null}b.callParent()},redirectTo:function(b,c){if(b.isModel){b=b.toUrl()}if(!c){var a=Ext.util.History.getToken();if(a===b){return false}}else{Ext.app.route.Router.onStateChange(b)}Ext.util.History.add(b);return true}},1,0,0,0,0,[[Ext.mixin.Observable.prototype.mixinId||Ext.mixin.Observable.$className,Ext.mixin.Observable]],[Ext.app,"BaseController"],0));(Ext.cmd.derive("Ext.app.Util",Ext.Base,{},0,0,0,0,0,0,[Ext.app,"Util"],function(){Ext.apply(Ext.app,{namespaces:{Ext:{}},addNamespaces:function(c){var d=Ext.app.namespaces,b,a;if(!Ext.isArray(c)){c=[c]}for(b=0,a=c.length;ba.length&&(c+"."===b.substring(0,c.length+1))){a=c}}return a===""?undefined:a},setupPaths:function(a,b,e){var d=Ext.manifest,c;if(a&&b!==null){d=d&&d.paths;if(!d||b!==undefined){Ext.Loader.setPath(a,(b===undefined)?"app":b)}}if(e){for(c in e){if(e.hasOwnProperty(c)){Ext.Loader.setPath(c,e[c])}}}}});Ext.getNamespace=Ext.app.getNamespace}));(Ext.cmd.derive("Ext.util.CollectionKey",Ext.Base,{isCollectionKey:true,observerPriority:-200,config:{collection:null,keyFn:null,property:null,rootProperty:null,unique:true},generation:0,map:null,mapRebuilds:0,constructor:function(a){this.initConfig(a)},get:function(a){var b=this.map||this.getMap();return b[a]||null},clear:function(){this.map=null},getRootProperty:function(){var b=this,a=(arguments.callee.$previous||Ext.Base.prototype.getRootProperty).call(this);return a!==null?a:b.getCollection().getRootProperty()},indexOf:function(k,e){var a=this.map||this.getMap(),l=a[k],g=this.getCollection(),b=g.length,d,h,j,c;if(!l){return -1}if(e===undefined){e=-1}if(l instanceof Array){j=l;h=b;for(c=j.length;c-->0;){d=g.indexOf(j[c]);if(de){h=d}}if(h===b){return -1}}else{h=g.indexOf(l)}return(h>e)?h:-1},updateKey:function(c,e){var b=this,d=b.map,g,a;if(d){g=d[e];if(g instanceof Array){a=Ext.Array.indexOf(g,c);if(a>=0){if(g.length>2){g.splice(a,1)}else{d[e]=g[1-a]}}}else{if(g){delete d[e]}}b.add([c])}},onCollectionAdd:function(b,a){if(this.map){this.add(a.items)}},onCollectionItemChange:function(b,a){this.map=null},onCollectionRefresh:function(){this.map=null},onCollectionRemove:function(e,d){var h=this,a=h.map,g=d.items,b=g.length,c,k,j;if(a){if(h.getUnique()&&be)?1:(b0&&v.getAutoSort(),r=v.getSource(),q=0,k=false,o=false,t,s,g,w,h,b;if(r&&!r.updating){r.itemChanged(u,i,m,n)}else{s=v.getKey(u);if(j){h=v.indexOfKey(c?m:s);o=(h<0);k=v.isItemFiltered(u);e=(o!==k)}if(e){if(k){q=[u];b=-1}else{w=[u];b=v.length}}else{if(a&&!k){if(!j){h=v.indexOfKey(c?m:s)}g=v.getSortFn();if(h!==-1){if(h&&g(p[h-1],p[h])>0){d=-1;b=Ext.Array.binarySearch(p,u,0,h,g)}else{if(h0){d=1;b=Ext.Array.binarySearch(p,u,h+1,g)}}if(d){w=[u]}}}}t={item:u,key:s,index:b,filterChanged:e,keyChanged:c,indexChanged:!!d,filtered:k,oldIndex:h,newIndex:b,wasFiltered:o,meta:n};if(c){t.oldKey=m}if(i){t.modified=i}v.beginUpdate();v.notify("beforeitemchange",[t]);if(c){v.updateKey(u,m)}if(w||q){v.splice(b,q,w)}if(d>0){t.newIndex--}else{if(d<0){t.oldIndex++}}v.notify(k?"filtereditemchange":"itemchange",[t]);v.endUpdate()}},remove:function(d){var c=this,a=c.decodeRemoveItems(arguments,0),b=c.length;c.splice(0,a);return b-c.length},removeAll:function(){var b=this,a=b.length;if(b.generation&&a){b.splice(0,a)}return b},removeAt:function(i,h){var j=this,b=j.length,e=Ext.Number,d=e.clipIndices(b,[i,(h===undefined)?1:h],e.Clip.COUNT),c=d[0],a=d[1]-c,k=(a===1)&&j.getAt(c),g;j.splice(c,a);g=j.length-b;return(k&&g)?k:g},removeByKey:function(a){var b=this.getByKey(a);if(!b||!this.remove(b)){return false}return b},replace:function(b){var a=this.indexOf(b);if(a===-1){this.add(b)}else{this.insert(a,b)}},splice:function(E,o,C){var m=this,d=m.sorted&&m.getAutoSort(),A=m.map,s=m.items,q=m.length,w=(o instanceof Array)?m.decodeRemoveItems(o):null,x=!w,F=Ext.Number,e=F.clipIndices(q,[E,x?o:0],F.Clip.COUNT),h=e[0],l=e[1],L=l-h,u=m.decodeItems(arguments,2),K=u?u.length:0,D,y,v,c=h,b=m.indices||((K||w)?m.getIndices():null),r=null,j=L?[h]:null,g=null,t=m.getSource(),a,H,I,N,B,G,M,p,z,J,P,O,l;if(t&&!t.updating){if(x){w=[];for(N=0;N1){if(!D.$cloned){u=D=D.slice(0)}m.sortData(D)}}for(N=0;N0;){p=m.getKey(w[N]);if((G=b[p])!==undefined){(j||(j=[])).push(G)}}if(!r&&!j){return m}m.beginUpdate();if(j){a=null;I=[];v={};if(j.length>1){j.sort(Ext.Array.numericSortFn)}for(N=0,J=j.length;N(a.at+H.length)){I.push(a={at:G,items:(H=[]),keys:(z=[]),map:v,next:a,replacement:r});if(r){r.replaced=a}}H.push(v[p]=B);z.push(p);if(G1&&G===h){--L;j[N--]=++h}}if(r){r.at=c}for(M=I.length;M-->0;){a=I[M];N=a.at;J=a.items.length;if(N+J1&&q){m.spliceMerge(D,g)}else{if(d){if(K>1){c=0;m.indices=b=null}else{c=O.findInsertionIndex(r.items[0],s,m.getSortFn())}}if(c===q){l=c;for(N=D.length-1;N>=0;--N){s[l+N]=D[N]}b=m.indices;if(b){for(N=0;N-1){c=g[b];a=this.indexOf(c);if(a>-1){return a+1}--b}return 0},onCollectionAdd:function(a,b){var l=this,o=b.atItem,k=b.items,g=l.requestedIndex,j,h,c,e,m,d;if(!l.sorted){if(g!==undefined){h=g}else{if(o){h=l.indexOf(o);if(h===-1){h=l.findInsertIndex(k[0])}else{++h}}else{h=0}}}if(l.getAutoFilter()&&l.filtered){for(e=0,d=k.length;ec)){c=j}}return[c,h]},count:function(a){return a.length},extremes:function(g,a,c,m,k){var h=null,b=null,e,n,j,d,l;for(e=a;ed)){d=l;b=n}}return[b,h]},max:function(e,g,d,h,c){var a=this._aggregators.bounds.call(this,e,g,d,h,c);return a[1]},maxItem:function(e,g,d,h,c){var a=this._aggregators.extremes.call(this,e,g,d,h,c);return a[1]},min:function(e,g,d,h,c){var a=this._aggregators.bounds.call(this,e,g,d,h,c);return a[0]},minItem:function(e,g,d,h,c){var a=this._aggregators.extremes.call(this,e,g,d,h,c);return a[0]},sum:function(c,g,b,j,a){for(var h,e=0,d=g;d1){Ext.Array.sort(c,b.prioritySortFn)}},prioritySortFn:function(g,e){var d=g.observerPriority||0,c=e.observerPriority||0;return d-c},applyExtraKeys:function(e,a){var g=this,d=a||{},c,b,h;for(b in e){h=e[b];if(!h.isCollectionKey){c={collection:g};if(Ext.isString(h)){c.property=h}else{c=Ext.apply(c,h)}h=new Ext.util.CollectionKey(c)}else{h.setCollection(g)}d[b]=g[b]=h;h.name=b}return d},applyGrouper:function(a){if(a){a=this.getSorters().decodeSorter(a,"Ext.util.Grouper")}return a},decodeItems:function(d,c){var g=this,b=(c===undefined)?d:d[c],a,h,e;if(!b||!b.$cloned){a=d.length>c+1||!Ext.isIterable(b);if(a){b=Ext.Array.slice(d,c);if(b.length===1&&b[0]===undefined){b.length=0}}h=g.getDecoder();if(h){if(!a){b=b.slice(0);a=true}for(e=b.length;e-->0;){if((b[e]=h.call(g,b[e]))===false){b.splice(e,1)}}}if(a){b.$cloned=true}}return b},getIndices:function(){var d=this,e=d.indices,a=d.items,g=a.length,c,b;if(!e){d.indices=e={};++d.indexRebuilds;for(c=0;c0);if(d||a){b.filtered=a;b.onFilterChange(c)}},getSortFn:function(){return this._sortFn||this.createSortFn()},getSorters:function(b){var a=this._sorters;if(!a&&b!==false){a=new Ext.util.SorterCollection();this.setSorters(a)}return a},onSortChange:function(){if(this.sorted){this.sortItems()}},sort:function(a,c,d){var b=this.getSorters();b.addSort.apply(b,arguments);return this},sortData:function(a){Ext.Array.sort(a,this.getSortFn());return a},sortItems:function(b){var a=this;if(a.sorted){b=a.getSortFn()}a.indices=null;a.notify("beforesort",[a.getSorters(false)]);if(a.length){Ext.Array.sort(a.items,b)}a.notify("sort")},sortBy:function(a){return this.sortItems(a)},findInsertionIndex:function(c,a,b){if(!a){a=this.items}if(!b){b=this.getSortFn()}return Ext.Array.binarySearch(a,c,b)},applySorters:function(a,b){if(a==null||(a&&a.isSorterCollection)){return a}if(a){if(!b){b=this.getSorters()}b.splice(0,b.length,a)}return b},createSortFn:function(){var c=this,a=c.getGrouper(),d=c.getSorters(false),b=d?d.getSortFn():null;if(!a){return b}return function(e,h){var g=a.sort(e,h);if(!g&&b){g=b(e,h)}return g}},updateGrouper:function(b){var c=this,a=c.getGroups(),e=c.getSorters(),d;c.onSorterChange();c.grouped=!!b;if(b){if(c.getTrackGroups()){if(!a){a=new Ext.util.GroupCollection({itemRoot:c.getRootProperty()});a.$groupable=c;c.setGroups(a)}a.setGrouper(b);d=true}}else{if(a){c.removeObserver(a);a.destroy()}c.setGroups(null)}if(!e.updating){c.onEndUpdateSorters(e)}if(d){a.onCollectionRefresh(c)}},updateSorters:function(a,b){var c=this;if(b){b.un("endupdate","onEndUpdateSorters",c)}if(a){a.on({endupdate:"onEndUpdateSorters",scope:c,priority:c.$endUpdatePriority});a.$sortable=c}c.onSorterChange();c.onEndUpdateSorters(a)},onSorterChange:function(){this._sortFn=null},onEndUpdateSorters:function(c){var b=this,d=b.sorted,a=(b.grouped&&b.getAutoGroup())||(c&&c.length>0);if(d||a){b.sorted=!!a;b.onSortChange(c)}},removeObserver:function(a){var b=this.observers;if(b){Ext.Array.remove(b,a)}},spliceMerge:function(k,s){var r=this,q=r.map,g=k.length,c=0,l=r.items,t=l.length,j=[],h=0,n=[],d=r.getSortFn(),m,b,o,e,p,a;r.items=n;for(a=0;a1){j[h-2].next=j[h-1]}for(;a1){j[h-2].next=j[h-1]}n.push(e);for(b=a+1;b=0){break}n.push(e);m.push(e)}}for(;c0)}h=v.createAssociationStore(a,p,r,j);h.$source=e;if(!r&&(v.autoLoad||g)){l=true}p[n]=h}if(g){if(l||h.isLoading()){h.on("load",function(x,w,y,i){d=[x,i];b=b||g.scope||p;if(y){Ext.callback(g.success,b,d)}else{Ext.callback(g.failure,b,d)}d.push(y);Ext.callback(g,b,d);Ext.callback(g.callback,b,d)},null,{single:true})}else{d=[h,null];b=b||g.scope||p;Ext.callback(g.success,b,d);d.push(true);Ext.callback(g,b,d);Ext.callback(g.callback,b,d)}}if(l&&!h.isLoading()){h.load()}return h},getAssociatedItem:function(b){var a=this.isMany?this.getStoreName():this.getInstanceName();return b[a]||null},onDrop:Ext.emptyFn,getReaderRoot:function(){var a=this;return a.associationKey||(a.associationKey=a.association.schema.getNamer().readerRoot(a.role))},getReader:function(){var c=this,a=c.reader,d=c.cls,e=!c.associationKey,b=this.getReaderRoot();if(a&&!a.isReader){if(Ext.isString(a)){a={type:a}}Ext.applyIf(a,{model:d,rootProperty:b,useSimpleAccessors:e,type:c.defaultReaderType});a=c.reader=Ext.createByAlias("reader."+a.type,a)}return a},getInstanceName:function(){var a=this;return a.instanceName||(a.instanceName=a.association.schema.getNamer().instanceName(a.role))},getOldInstanceName:function(){return this.oldInstanceName||(this.oldInstanceName="$old"+this.getInstanceName())},getStoreName:function(){var a=this;return a.storeName||(a.storeName=a.association.schema.getNamer().storeName(a.role))},constructReader:function(e){var g=this,a=g.getReader(),h=g.cls,i=!g.associationKey,b=g.getReaderRoot(),c,d;if(!a){d=h.getProxy();if(d){c=d.getReader();a=new c.self();a.copyFrom(c);a.setRootProperty(b)}else{a=new e.self({model:h,useSimpleAccessors:i,rootProperty:b})}g.reader=a}return a},read:function(c,g,d,e){var a=this.constructReader(d),b=a.getRoot(g);if(b){return a.readRecords(b,e,this._internalReadOptions)}},getCallbackOptions:function(a,b,c){if(typeof a==="function"){a={callback:a,scope:b||c}}else{if(a){a=Ext.apply({},a);a.scope=b||a.scope||c}}return a},doGetFK:function(b,n,l){var i=this,m=i.cls,e=i.association.getFieldName(),k=i.getInstanceName(),j=b[k],a=n&&n.reload,d=j!==undefined&&!a,h=b.session,c,g;if(!d){if(h){c=b.get(e);if(c||c===0){d=h.peekRecord(m,c,true)&&!a;j=h.getRecord(m,c,false)}else{d=true;b[k]=j=null}}else{if(e){c=b.get(e);if(!c&&c!==0){d=true;b[k]=j=null}else{if(!j){j=m.createWithId(c)}}}else{d=true}}}else{if(j){d=!j.isLoading()}}if(d){if(n){g=[j,null];l=l||n.scope||b;Ext.callback(n.success,l,g);g.push(true);Ext.callback(n,l,g);Ext.callback(n.callback,l,g)}}else{b[k]=j;n=i.getCallbackOptions(n,l,b);j.load(n)}return j},doSetFK:function(a,j,n,m){var i=this,g=i.association.getFieldName(),k=i.getInstanceName(),e=a[k],c=i.inverse,d=c.setterName,h=a.session,l,b;if(j&&j.isEntity){if(e!==j){b=i.getOldInstanceName();a[b]=e;a[k]=j;if(e&&e.isEntity){e[c.getInstanceName()]=undefined}if(g){a.set(g,j.getId())}delete a[b];if(d){j[d](a)}}}else{l=(a.changingKey&&!c.isMany)||a.set(g,j);if(l&&e&&e.isEntity&&!e.isEqual(e.getId(),j)){a[k]=undefined;if(!c.isMany){e[c.getInstanceName()]=undefined}}}if(n){if(Ext.isFunction(n)){n={callback:n,scope:m||a}}return a.save(n)}}},1,0,0,0,0,0,[Ext.data.schema,"Role"],0));(Ext.cmd.derive("Ext.data.schema.Association",Ext.Base,{isOneToOne:false,isManyToOne:false,isManyToMany:false,owner:null,field:null,constructor:function(a){var c=this,d,b;Ext.apply(c,a);c.left=d=new c.Left(c,c.left);c.right=b=new c.Right(c,c.right);d.inverse=b;b.inverse=d},hasField:function(){return !!this.field},getFieldName:function(){var a=this.field;return a?a.name:""}},1,0,0,0,0,0,[Ext.data.schema,"Association"],0));(Ext.cmd.derive("Ext.data.schema.OneToOne",Ext.data.schema.Association,{isOneToOne:true,isToOne:true,kind:"one-to-one",Left:Ext.define(null,{extend:"Ext.data.schema.Role",onDrop:function(a,b){var c=this.getAssociatedItem(a);a[this.getInstanceName()]=null;if(c){c[this.inverse.getInstanceName()]=null}},createGetter:function(){var a=this;return function(){return a.doGet(this)}},createSetter:function(){var a=this;return function(b){return a.doSet(this,b)}},doGet:function(a){var b=this.getInstanceName(),c=a[b],d=a.session;if(!c&&d){}return c||null},doSet:function(a,e){var b=this.getInstanceName(),d=a[b],c=this.inverse.setterName;if(d!==e){a[b]=e;if(c){e[c](a)}}return d},read:function(a,d,b,e){var c=this,g=c.callParent([a,d,b,e]),h;if(g){h=g[0];if(h){h[c.inverse.getInstanceName()]=a;a[c.getInstanceName()]=h;delete a.data[c.role]}}}}),Right:Ext.define(null,{extend:"Ext.data.schema.Role",left:false,side:"right",createGetter:function(){var a=this;return function(b,c){return a.doGetFK(this,b,c)}},createSetter:function(){var a=this;return function(d,b,c){return a.doSetFK(this,d,b,c)}},onDrop:function(g,d){var b=this,c=b.association.field,a=b.getAssociatedItem(g),e;if(b.inverse.owner){if(d){e=g.get(c.name);if(e||e===0){a=d.getEntry(b.cls,e).record;if(a){a.drop()}}}else{if(a){a.drop()}}}if(c){g.set(c.name,null)}g[b.getInstanceName()]=null;if(a){a[b.inverse.getInstanceName()]=null}},onValueChange:function(i,h,g){var e=this,a=i[e.getOldInstanceName()]||e.getAssociatedItem(i),d=g||g===0,c=e.getInstanceName(),b=e.cls;i.changingKey=true;e.doSetFK(i,g);if(!d){i[c]=null}else{if(h&&b){i[c]=h.peekRecord(b,g)||undefined}}if(e.inverse.owner&&a){e.association.schema.queueKeyCheck(a,e)}i.changingKey=false},checkKeyForDrop:function(a){var b=this.inverse.getAssociatedItem(a);if(!b){a.drop()}},read:function(d,e,p,g){var j=this,n=j.callParent([d,e,p,g]),k,m,o,i,l,b,c,a,h;if(n){k=n[0];m=j.association.field;if(m){o=m.name}i=d.session;h=d.data;if(k){if(i){l=i.getRefs(k,this.inverse,true);a=(l&&l[d.id])||(h[o]===undefined)}else{a=true}if(a){if(m){c=h[o];b=k.id;if(c!==b){h[o]=b;if(i){i.updateReference(d,m,b,c)}}}k[j.inverse.getInstanceName()]=d;d[j.getInstanceName()]=k}delete h[j.role]}}}})},0,0,0,0,0,0,[Ext.data.schema,"OneToOne"],0));(Ext.cmd.derive("Ext.data.schema.ManyToOne",Ext.data.schema.Association,{isManyToOne:true,isToOne:true,kind:"many-to-one",Left:Ext.define(null,{extend:"Ext.data.schema.Role",isMany:true,onDrop:function(h,e){var g=this,k=g.getAssociatedItem(h),b,d,c,j,a;if(k){b=k.removeAll();if(b&&g.inverse.owner){for(c=0,d=b.length;c0){d=e[j];a=!d.isEqual(b,d.get(k));c=m?null:l;if(a!==m){d.changingKey=true;d[g](c);d.changingKey=false}else{d[n]=c}}}}),Right:Ext.define(null,{extend:"Ext.data.schema.Role",left:false,side:"right",onDrop:function(c,b){var a=this.association.field;if(a){c.set(a.name,null)}c[this.getInstanceName()]=null},createGetter:function(){var a=this;return function(b,c){return a.doGetFK(this,b,c)}},createSetter:function(){var a=this;return function(b,c,d){return a.doSetFK(this,b,c,d)}},checkMembership:function(c,d){var b=this.association.field,a;a=this.getSessionStore(c,d.get(b.name));if(a&&!a.contains(d)){a.add(d)}},onValueChange:function(d,j,b,a){var k=this,n=k.getInstanceName(),p=k.cls,c,o,m,e,g,l,h;if(!d.changingKey){c=b||b===0;if(!c){d[n]=null}if(j){m=k.getSessionStore(j,a);if(m){m.remove(d)}if(c){m=k.getSessionStore(j,b);if(m&&!m.isLoading()){m.add(d)}if(p){h=j.peekRecord(p,b)}d[n]=h||undefined}}else{o=d.joined;if(o){for(e=0,g=o.length;e=0){a.remove([b])}}else{if(b<0){c=a.getSession().getEntry(this.type,h);g=c&&c.record;if(g){a.add(g)}}}a.matrixUpdate=0}},adoptAssociated:function(b,g){var d=this.getAssociatedItem(b),c,e,a;if(d){d.setSession(g);this.onStoreCreate(d,g,b.getId());c=d.getData().items;for(e=0,a=c.length;e1){c[a]=this.apply("capitalize",c[a])}return c.join("")},getterName:function(b){var a=b.role;if(b&&b.isMany){return a}return"get"+this.apply("capitalize",a)},inverseFieldRole:function(h,i,e,b){var g=this,a=g.apply(i?"uniRole":"multiRole",h),d=g.apply("pluralize",e),c=g.apply("undotted,pluralize",b);if(d.toLowerCase()!==c.toLowerCase()){a=e+g.apply("capitalize",a)}return a},manyToMany:function(e,d,a){var c=this,b=c.apply("undotted,capitalize,singularize",d)+c.apply("undotted,capitalize,pluralize",a);if(e){b=c.apply("capitalize",e+b)}return b},manyToOne:function(d,b,a,c){return this.apply("capitalize,singularize",a)+this.apply("capitalize",b)},matrixRole:function(c,b){var a=this.apply(c?"multiRole,capitalize":"multiRole",b);return c?c+a:a},oneToOne:function(d,b,a,c){return this.apply("undotted,capitalize,singularize",a)+this.apply("capitalize",b)},setterName:function(a){return"set"+this.apply("capitalize",a.role)},endsWithIdRe:/(?:(_id)|[^A-Z](Id))$/,cache:{},apply:function(e,c){var j=this,b=j.cache,k=b[c]||(b[c]={}),h=k[e],g,d,a;if(!h){if(e.indexOf(",")<0){h=j[e](c)}else{d=(a=e.split(",")).length;h=c;for(g=0;g=Math.max(b,a)},find:function(g,e,h,i,a,d){var c=!i,b=!!(c&&d);return this.getData().findIndex(g,e,h,c,b,!a)},findRecord:function(){var b=this,a=b.find.apply(b,arguments);return a!==-1?b.getAt(a):null},findExact:function(b,a,c){return this.getData().findIndexBy(function(d){return d.isEqual(d.get(b),a)},this,c)},findBy:function(b,a,c){return this.getData().findIndexBy(b,a,c)},getAt:function(a){return this.getData().getAt(a)||null},getRange:function(d,b,c){var a=this.getData().getRange(d,Ext.isNumber(b)?b+1:b);if(c&&c.callback){c.callback.call(c.scope||this,a,d,b,c)}return a},getFilters:function(b){var a=(arguments.callee.$previous||Ext.Base.prototype.getFilters).call(this);if(!a&&b!==false){this.setFilters([]);a=(arguments.callee.$previous||Ext.Base.prototype.getFilters).call(this)}return a},applyFilters:function(b,a){var c;if(!a){a=this.createFiltersCollection();c=true}a.add(b);if(c){this.onRemoteFilterSet(a,this.getRemoteFilter())}return a},getSorters:function(b){var a=(arguments.callee.$previous||Ext.Base.prototype.getSorters).call(this);if(!a&&b!==false){this.setSorters([]);a=(arguments.callee.$previous||Ext.Base.prototype.getSorters).call(this)}return a},applySorters:function(b,a){var c;if(!a){a=this.createSortersCollection();c=true}a.add(b);if(c){this.onRemoteSortSet(a,this.getRemoteSort())}return a},filter:function(b,c,a){if(Ext.isString(b)){b={property:b,value:c}}this.suppressNextFilter=!!a;this.getFilters().add(b);this.suppressNextFilter=false},removeFilter:function(a,d){var c=this,b=c.getFilters();c.suppressNextFilter=!!d;if(a instanceof Ext.util.Filter){b.remove(a)}else{b.removeByKey(a)}c.suppressNextFilter=false},updateRemoteSort:function(a){this.onRemoteSortSet(this.getSorters(false),a)},updateRemoteFilter:function(a){this.onRemoteFilterSet(this.getFilters(false),a)},addFilter:function(b,a){this.suppressNextFilter=!!a;this.getFilters().add(b);this.suppressNextFilter=false},filterBy:function(b,a){this.getFilters().add({filterFn:b,scope:a||this})},clearFilter:function(c){var b=this,a=b.getFilters(false);if(!a||a.getCount()===0){return}b.suppressNextFilter=!!c;a.removeAll();b.suppressNextFilter=false},isFiltered:function(){return this.getFilters().getCount()>0},isSorted:function(){var a=this.getSorters(false);return !!(a&&a.length>0)||this.isGrouped()},addFieldTransform:function(e){if(e.getTransform()){return}var d=e.getProperty(),c=this.getModel(),a,b;if(c){a=c.getField(d);b=a?a.getSortType():null}if(b&&b!==Ext.identityFn){e.setTransform(b)}},beginUpdate:function(){if(!this.updating++){this.fireEvent("beginupdate")}},endUpdate:function(){if(this.updating&&!--this.updating){this.fireEvent("endupdate");this.onEndUpdate()}},getState:function(){var e=this,g=[],d=e.getFilters(),b=e.getGrouper(),h,c,a;e.getSorters().each(function(i){g[g.length]=i.getState();c=true});if(e.statefulFilters&&e.saveStatefulFilters){c=true;h=[];d.each(function(i){h[h.length]=i.getState()})}if(b){c=true}if(c){a={};if(g.length){a.sorters=g}if(h){a.filters=h}if(b){a.grouper=b.getState()}}return a},applyState:function(e){var c=this,b=e.sorters,a=e.filters,d=e.grouper;if(b){c.getSorters().replaceAll(b)}if(a){c.saveStatefulFilters=true;c.getFilters().replaceAll(a)}if(d){c.setGrouper(d)}},hasPendingLoad:Ext.emptyFn,isLoaded:Ext.emptyFn,isLoading:Ext.emptyFn,destroy:function(){var a=this;if(a.getStoreId()){Ext.data.StoreManager.unregister(a)}a.callParent();a.onDestroy()},sort:function(c,b,d){var a=this;if(arguments.length===0){if(a.getRemoteSort()){a.load()}else{a.forceLocalSort()}}else{a.getSorters().addSort(c,b,d)}},onBeforeCollectionSort:function(a,b){if(b){this.fireEvent("beforesort",this,b.getRange())}},onSorterEndUpdate:function(){var a=this,b;b=a.getSorters(false);if(a.settingGroups||!b){return}b=b.getRange();if(b.length){if(a.getRemoteSort()){a.load({callback:function(){a.fireEvent("sort",a,b)}})}else{a.fireEvent("datachanged",a);a.fireEvent("refresh",a);a.fireEvent("sort",a,b)}}else{a.fireEvent("sort",a,b)}},onFilterEndUpdate:function(){var b=this,a=b.suppressNextFilter;if(b.getRemoteFilter()){b.currentPage=1;if(!a){b.load()}}else{if(!a){b.fireEvent("datachanged",b);b.fireEvent("refresh",b)}}if(b.trackStateChanges){b.saveStatefulFilters=true}b.fireEvent("filterchange",b,b.getFilters().getRange())},updateGroupField:function(a){if(a){this.setGrouper({property:a,direction:this.getGroupDir()})}else{this.setGrouper(null)}},getGrouper:function(){return this.getData().getGrouper()},group:function(a,d){var b=this,c=b.getSorters(false),e=a||(c&&c.length);if(a&&typeof a==="string"){a={property:a,direction:d||b.getGroupDir()}}b.settingGroups=true;b.getData().setGrouper(a);delete b.settingGroups;if(e){if(b.getRemoteSort()){b.load({scope:b,callback:b.fireGroupChange})}else{b.fireEvent("datachanged",b);b.fireEvent("refresh",b);b.fireGroupChange()}}else{b.fireGroupChange()}},fireGroupChange:function(){if(!this.destroyed){this.fireEvent("groupchange",this,this.getGrouper())}},clearGrouping:function(){this.group(null)},getGroupField:function(){var a=this.getGrouper(),b="";if(a){b=a.getProperty()}return b},isGrouped:function(){return !!this.getGrouper()},applyGrouper:function(a){this.group(a);return this.getData().getGrouper()},getGroups:function(){return this.getData().getGroups()},onEndUpdate:Ext.emptyFn,privates:{loadsSynchronously:Ext.privateFn,onRemoteFilterSet:function(a,b){if(a){a[b?"on":"un"]("endupdate",this.onFilterEndUpdate,this)}},onRemoteSortSet:function(b,c){var a=this;if(b){b[c?"on":"un"]("endupdate",a.onSorterEndUpdate,a);a.getData()[c?"un":"on"]("beforesort",a.onBeforeCollectionSort,a)}}},deprecated:{5:{methods:{destroyStore:function(){this.destroy()}}}}},1,0,0,0,0,[[Ext.mixin.Observable.prototype.mixinId||Ext.mixin.Observable.$className,Ext.mixin.Observable],[Ext.mixin.Factoryable.prototype.mixinId||Ext.mixin.Factoryable.$className,Ext.mixin.Factoryable]],[Ext.data,"AbstractStore"],0));(Ext.cmd.derive("Ext.data.Error",Ext.Base,{isError:true,$configPrefixed:false,config:{field:null,message:""},constructor:function(a){this.initConfig(a);this.msg=this.message}},1,0,0,0,0,0,[Ext.data,"Error"],0));(Ext.cmd.derive("Ext.data.ErrorCollection",Ext.util.MixedCollection,{alternateClassName:"Ext.data.Errors",init:function(g){var k=this,h=g.fields,d=g.data,m,l,n,e,j,c,b,a;for(e=0,j=h.length;e]+>/gi,asText:function(a){return(a!=null)?String(a).replace(this.stripTagsRe,""):"\x00"},asUCText:function(a){return(a!=null)?String(a).toUpperCase().replace(this.stripTagsRe,""):"\x00"},asUCString:function(a){return(a!=null)?String(a).toUpperCase():"\x00"},asDate:function(a){if(!a){return 0}if(Ext.isDate(a)){return a.getTime()}return Date.parse(String(a))},asFloat:function(a){var b=parseFloat(String(a).replace(this.stripCommasRe,""));return isNaN(b)?0:b},asInt:function(a){var b=parseInt(String(a).replace(this.stripCommasRe,""),10);return isNaN(b)?0:b}},0,0,0,0,0,0,[Ext.data,"SortTypes"],0));(Ext.cmd.derive("Ext.data.validator.Validator",Ext.Base,{isValidator:true,type:"base",statics:{all:{},register:function(b,a){var c=this.all;c[b.toUpperCase()]=c[b.toLowerCase()]=c[b]=a.prototype}},onClassExtended:function(a,b){if(b.type){Ext.data.validator.Validator.register(b.type,a)}},constructor:function(a){if(typeof a==="function"){this.fnOnly=true;this.validate=a}else{this.initConfig(a)}},validate:function(){return true},clone:function(){var a=this;if(a.fnOnly){return new Ext.data.validator.Validator(a.validate)}return new a.self(a.getCurrentConfig())}},1,0,0,0,["data.validator.base"],[[Ext.mixin.Factoryable.prototype.mixinId||Ext.mixin.Factoryable.$className,Ext.mixin.Factoryable]],[Ext.data.validator,"Validator"],function(){this.register(this.prototype.type,this)}));(Ext.cmd.derive("Ext.data.field.Field",Ext.Base,{alternateClassName:"Ext.data.Field",aliasPrefix:"data.field.",type:"auto",factoryConfig:{defaultProperty:"name"},isDataField:true,isField:true,allowBlank:true,allowNull:false,critical:false,defaultInvalidMessage:"This field is invalid",defaultValue:undefined,definedBy:null,depends:null,dependents:null,mapping:null,name:null,ordinal:undefined,persist:null,reference:null,unique:false,rank:null,stripRe:/[\$,%]/g,calculated:false,evil:false,identifier:false,onClassExtended:function(c,e){var g=e.sortType,d=c.prototype,a=d.validators,b=e.validators;if(g&&Ext.isString(g)){d.sortType=Ext.data.SortTypes[g]}if(b){if(!Ext.isArray(b)){b=[b]}delete e.validators;if(a){b=a.concat(b)}d.validators=b}},argumentNamesRe:/^function\s*\(\s*([^,\)\s]+)/,calculateRe:/[^\.a-z0-9_]([a-z_][a-z_0-9]*)\.([a-z_][a-z_0-9]*)/gi,constructor:function(d){var l=this,n=l.calculateRe,h,o,m,i,c,a,j,b,k,e,g;if(d){if(Ext.isString(d)){l.name=d}else{g=d.validators;if(g){delete d.validators;l.instanceValidators=g}Ext.apply(l,d)}}if(!l.allowNull){l.allowNull=!!l.reference}h=l.calculate;c=l.depends;if(h){l.convert=l.doCalculate;if(!c){if(!(c=h.$depends)){a={};k=h.toString();h.$depends=c=[];j=l.argumentNamesRe.exec(k);b=j?j[1]:"data";while((j=n.exec(k))){if(b===j[1]&&!a[e=j[2]]){a[e]=1;c.push(e)}}}l.depends=c}}m=l.defaultValue;if(l.convert){l.calculated=o=l.convert.length>1;l.evil=o&&!c}if(l.persist===null){l.persist=!h}i=l.sortType;if(!l.sortType){l.sortType=Ext.data.SortTypes.none}else{if(Ext.isString(i)){l.sortType=Ext.data.SortTypes[i]}}if(c&&typeof c==="string"){l.depends=[c]}l.cloneDefaultValue=m!==undefined&&(Ext.isDate(m)||Ext.isArray(m)||Ext.isObject(m))},setModelValidators:function(a){this._validators=null;this.modelValidators=a},compileValidators:function(){var a=this;a._validators=[];a.constructValidators(a.validators);a.constructValidators(a.modelValidators);a.constructValidators(a.instanceValidators)},constructValidators:function(a){if(a){if(!(a instanceof Array)){a=[a]}var e=a.length,c=this._validators,b,d;for(b=0;b0){(n=x[C]).dirty=true;z=z?Math.min(z,n.rank):n.rank}if(!c||c.persist){if(o&&o.hasOwnProperty(J)){if(!p||j.isEqual(o[J],A)){delete o[J];H.dirty=-1}}else{if(p){if(!o){H.modified=o={}}H.dirty=true;o[J]=t}}}if(J===H.idField.name){D=true;y=t;u=A}}if(!z){break}c=m[z-1];c.dirty=false;if(G){delete e[g]}else{e=H._singleProp;G=true}g=c.name;e[g]=I[g];r=true;for(;z0;){b=(m=c[d]).name;if(!(b in j)){q=e[b];if(r&&m.serialize){q=m.serialize(q,k)}j[b]=q}}}if(l){k.getAssociatedData(j,a)}return j},getTransientFields:function(){var a=this.self,b=a.transientFields;if(!b){a.rankFields();b=a.transientFields}return b},isLoading:function(){return !!this.loadOperation},abort:function(){var a=this.loadOperation;if(a){a.abort()}},load:function(b){b=Ext.apply({},b);var g=this,e=b.scope||g,c=g.getProxy(),i=b.callback,a=g.loadOperation,h=g.getId(),d;if(a){d=a.extraCalls;if(!d){d=a.extraCalls=[]}d.push(b);return a}b.id=h;b.recordCreator=function(l,j,k){var m=g.session;if(k){k.recordCreator=m?m.recordCreator:null}g.set(l,g._commitOptions);return g};b.internalCallback=function(l){var p=l.wasSuccessful()&&l.getRecords().length>0,q=g.loadOperation,n=q.extraCalls,k=[g,l],o=[g,l,p],m,j;g.loadOperation=null;if(p){Ext.callback(b.success,e,k)}else{Ext.callback(b.failure,e,k)}Ext.callback(i,e,o);if(n){for(m=0,j=n.length;m0;){a=k[d];if(a in c){delete c[a];delete m[a]}}for(d=0,h=g.length;d=j.getTotal()){j.setConfig({success:false,records:[],total:0})}else{j.setRecords(Ext.Array.slice(d,b,b+e))}}g.setCompleted()}},clear:Ext.emptyFn},0,0,0,0,["proxy.memory"],0,[Ext.data.proxy,"Memory",Ext.data,"MemoryProxy"],0));(Ext.cmd.derive("Ext.data.ProxyStore",Ext.data.AbstractStore,{config:{model:undefined,fields:null,proxy:undefined,autoLoad:undefined,autoSync:false,batchUpdateMode:"operation",sortOnLoad:true,trackRemoved:true,asynchronousLoad:undefined},onClassExtended:function(b,d,a){var c=d.model,e;if(typeof c==="string"){e=a.onBeforeCreated;a.onBeforeCreated=function(){var h=this,g=arguments;Ext.require(c,function(){e.apply(h,g)})}}},implicitModel:"Ext.data.Model",autoSyncSuspended:0,constructor:function(a){var b=this;b.removed=[];Ext.data.AbstractStore.prototype.constructor.apply(this,arguments);if(b.getAsynchronousLoad()===false){b.flushLoad()}},applyAsynchronousLoad:function(a){if(a==null){a=!this.loadsSynchronously()}return a},updateAutoLoad:function(a){this.getData();if(a){this.load(Ext.isObject(a)?a:undefined)}},getTotalCount:function(){return this.totalCount||0},applyFields:function(a){if(a){this.createImplicitModel(a)}},applyModel:function(a){if(a){a=Ext.data.schema.Schema.lookupEntity(a)}else{this.getFields();a=this.getModel()||this.createImplicitModel()}return a},applyProxy:function(b){var a=this.getModel();if(b!==null){if(b){if(b.isProxy){b.setModel(a)}else{if(Ext.isString(b)){b={type:b,model:a}}else{if(!b.model){b=Ext.apply({model:a},b)}}b=Ext.createByAlias("proxy."+b.type,b);b.autoCreated=true}}else{if(a){b=a.getProxy()}}if(!b){b=Ext.createByAlias("proxy.memory");b.autoCreated=true}}return b},applyState:function(b){var a=this;Ext.data.AbstractStore.prototype.applyState.call(this,b);if(a.getAutoLoad()||a.isLoaded()){a.load()}},updateProxy:function(b,a){this.proxyListeners=Ext.destroy(this.proxyListeners)},updateTrackRemoved:function(a){this.cleanRemoved();this.removed=a?[]:null},onMetaChange:function(a,b){this.fireEvent("metachange",this,b)},create:function(e,c){var d=this,g=d.getModel(),a=new g(e),b;c=Ext.apply({},c);if(!c.records){c.records=[a]}c.internalScope=d;c.internalCallback=d.onProxyWrite;b=d.createOperation("create",c);return b.execute()},read:function(){return this.load.apply(this,arguments)},update:function(b){var c=this,a;b=Ext.apply({},b);if(!b.records){b.records=c.getUpdatedRecords()}b.internalScope=c;b.internalCallback=c.onProxyWrite;a=c.createOperation("update",b);return a.execute()},onProxyWrite:function(b){var c=this,d=b.wasSuccessful(),a=b.getRecords();switch(b.getAction()){case"create":c.onCreateRecords(a,b,d);break;case"update":c.onUpdateRecords(a,b,d);break;case"destroy":c.onDestroyRecords(a,b,d);break}if(d){c.fireEvent("write",c,b);c.fireEvent("datachanged",c)}},onCreateRecords:Ext.emptyFn,onUpdateRecords:Ext.emptyFn,onDestroyRecords:function(b,a,c){if(c){this.cleanRemoved()}},erase:function(b){var c=this,a;b=Ext.apply({},b);if(!b.records){b.records=c.getRemovedRecords()}b.internalScope=c;b.internalCallback=c.onProxyWrite;a=c.createOperation("destroy",b);return a.execute()},onBatchOperationComplete:function(b,a){return this.onProxyWrite(a)},onBatchComplete:function(c,a){var g=this,b=c.operations,e=b.length,d;if(g.batchUpdateMode!=="operation"){g.suspendEvents();for(d=0;d0){b.create=g;h=true}if(d.length>0){b.update=d;h=true}if(a.length>0){b.destroy=a;h=true}if(h&&e.fireEvent("beforesync",b)!==false){e.isSyncing=true;c=c||{};e.proxy.batch(Ext.apply(c,{operations:b,listeners:e.getBatchListeners()}))}return e},getBatchListeners:function(){var b=this,a={scope:b,exception:b.onBatchException,complete:b.onBatchComplete};if(b.batchUpdateMode==="operation"){a.operationcomplete=b.onBatchOperationComplete}return a},save:function(){return this.sync.apply(this,arguments)},load:function(a){var b=this;if(typeof a==="function"){a={callback:a}}else{a=a?Ext.Object.chain(a):{}}b.pendingLoadOptions=a;if(b.getAsynchronousLoad()){if(!b.loadTimer){b.loadTimer=Ext.asap(b.flushLoad,b)}}else{b.flushLoad()}return b},flushLoad:function(){var c=this,b=c.pendingLoadOptions,a;c.clearLoadTask();if(!b){return}c.setLoadOptions(b);if(c.getRemoteSort()&&b.sorters){c.fireEvent("beforesort",c,b.sorters)}a=Ext.apply({internalScope:c,internalCallback:c.onProxyLoad,scope:c},b);c.lastOptions=a;a=c.createOperation("read",a);if(c.fireEvent("beforeload",c,a)!==false){c.onBeforeLoad(a);c.loading=true;a.execute()}},reload:function(a){var b=Ext.apply({},a,this.lastOptions);return this.load(b)},onEndUpdate:function(){var a=this;if(a.needsSync&&a.autoSync&&!a.autoSyncSuspended){a.sync()}},afterReject:function(a){var b=this;if(b.contains(a)){b.onUpdate(a,Ext.data.Model.REJECT,null);b.fireEvent("update",b,a,Ext.data.Model.REJECT,null)}},afterCommit:function(a,c){var b=this;if(!c){c=null}if(b.contains(a)){b.onUpdate(a,Ext.data.Model.COMMIT,c);b.fireEvent("update",b,a,Ext.data.Model.COMMIT,c)}},afterErase:function(a){this.onErase(a)},onErase:Ext.emptyFn,onUpdate:Ext.emptyFn,onDestroy:function(){var b=this,a=b.getProxy();b.clearLoadTask();b.getData().destroy();b.data=null;b.setProxy(null);if(a.autoCreated){a.destroy()}b.setModel(null)},hasPendingLoad:function(){return !!this.pendingLoadOptions||this.isLoading()},isLoading:function(){return !!this.loading},isLoaded:function(){return this.loadCount>0},suspendAutoSync:function(){++this.autoSyncSuspended},resumeAutoSync:function(b){var a=this;if(a.autoSyncSuspended&&!--a.autoSyncSuspended){if(b){a.sync()}}},removeAll:Ext.emptyFn,clearData:Ext.emptyFn,privates:{getRawRemovedRecords:function(){return this.removed},onExtraParamsChanged:function(){},clearLoadTask:function(){Ext.asapCancel(this.loadTimer);this.pendingLoadOptions=this.loadTimer=null},cleanRemoved:function(){var c=this.getRawRemovedRecords(),a,b;if(c){for(b=0,a=c.length;b-1},each:function(e,d){var g=this.data.items,a=g.length,b,c;for(c=0;c0){if(l){j=0;if(a.length>1&&k){j=1}u[g]=a[j].getProperty();u[o]=a[j].getDirection()}else{u[g]=v.encodeSorters(a)}}if(q&&n&&n.length>0){u[q]=v.encodeFilters(n)}return u},buildUrl:function(c){var b=this,a=b.getUrl(c);if(b.getNoCache()){a=Ext.urlAppend(a,Ext.String.format("{0}={1}",b.getCacheString(),Ext.Date.now()))}return a},getUrl:function(b){var a;if(b){a=b.getUrl()||this.getApi()[b.getAction()]}return a?a:(arguments.callee.$previous||Ext.data.proxy.Proxy.prototype.getUrl).call(this)},doRequest:function(a){},afterRequest:Ext.emptyFn,destroy:function(){Ext.data.proxy.Proxy.prototype.destroy.call(this);Ext.destroy(this.getReader(),this.getWriter());this.reader=this.writer=null}},0,0,0,0,["proxy.server"],0,[Ext.data.proxy,"Server",Ext.data,"ServerProxy"],0));(Ext.cmd.derive("Ext.data.proxy.Ajax",Ext.data.proxy.Server,{alternateClassName:["Ext.data.HttpProxy","Ext.data.AjaxProxy"],isAjaxProxy:true,defaultActionMethods:{create:"POST",read:"GET",update:"POST",destroy:"POST"},config:{binary:false,headers:undefined,paramsAsJson:false,withCredentials:false,useDefaultXhrHeader:true,username:null,password:null,actionMethods:{create:"POST",read:"GET",update:"POST",destroy:"POST"}},doRequest:function(a){var d=this,e=d.getWriter(),c=d.buildRequest(a),h=d.getMethod(c),b,g;if(e&&a.allowWrite()){c=e.write(c)}c.setConfig({binary:d.getBinary(),headers:d.getHeaders(),timeout:d.getTimeout(),scope:d,callback:d.createRequestCallback(c,a),method:h,useDefaultXhrHeader:d.getUseDefaultXhrHeader(),disableCaching:false});if(h.toUpperCase()!=="GET"&&d.getParamsAsJson()){g=c.getParams();if(g){b=c.getJsonData();if(b){b=Ext.Object.merge({},b,g)}else{b=g}c.setJsonData(b);c.setParams(undefined)}}if(d.getWithCredentials()){c.setWithCredentials(true);c.setUsername(d.getUsername());c.setPassword(d.getPassword())}return d.sendRequest(c)},sendRequest:function(a){a.setRawRequest(Ext.Ajax.request(a.getCurrentConfig()));this.lastRequest=a;return a},abort:function(a){a=a||this.lastRequest;if(a){Ext.Ajax.abort(a.getRawRequest())}},getMethod:function(a){var c=this.getActionMethods(),b=a.getAction(),d;if(c){d=c[b]}return d||this.defaultActionMethods[b]},createRequestCallback:function(c,a){var b=this;return function(e,g,d){if(c===b.lastRequest){b.lastRequest=null}b.processResponse(g,a,c,d)}},destroy:function(){this.lastRequest=null;Ext.data.proxy.Server.prototype.destroy.call(this)}},0,0,0,0,["proxy.ajax"],0,[Ext.data.proxy,"Ajax",Ext.data,"HttpProxy",Ext.data,"AjaxProxy"],0));(Ext.cmd.derive("Ext.data.reader.Json",Ext.data.reader.Reader,{alternateClassName:"Ext.data.JsonReader",config:{record:null,metaProperty:"metaData",useSimpleAccessors:false,preserveRawData:false},updateRootProperty:function(){this.forceBuildExtractors()},updateMetaProperty:function(){this.forceBuildExtractors()},readRecords:function(d,c,a){var b=this,e;if(b.getMeta){e=b.getMeta(d);if(e){b.onMetaChange(e)}}else{if(d.metaData){b.onMetaChange(d.metaData)}}return Ext.data.reader.Reader.prototype.readRecords.call(this,d,c,a)},getResponseData:function(a){var b;try{return Ext.decode(a.responseText)}catch(c){b=this.createReadError(c.message);Ext.Logger.warn("Unable to parse the JSON returned by the server");this.fireEvent("exception",this,a,b);return b}},buildExtractors:function(){var c=this,a,b;if(Ext.data.reader.Reader.prototype.buildExtractors.apply(this,arguments)){a=c.getMetaProperty();b=c.getRootProperty();if(b){c.getRoot=c.getAccessor(b)}else{c.getRoot=Ext.identityFn}if(a){c.getMeta=c.getAccessor(a)}}},extractData:function(a,e){var g=this.getRecord(),d=[],c,b;if(g){c=a.length;if(!c&&Ext.isObject(a)){c=1;a=[a]}for(b=0;b1||(q&&!o)){d+=m}else{if(l){s=false;if(p){++q}else{if(o){--q;s=true}}if(d){if(s){d="["+d+"]"}else{d="."+d}k+=d;g.push(""+k);d=""}}}}u=g.join(" && ");u=Ext.functionFactory("raw","return "+u)}return u}}()),createFieldAccessor:function(e){var b=this,a=e.mapping,c=a||a===0,d=c?a:e.name;if(c){if(typeof d==="function"){return function(g){return e.mapping(g,b)}}else{return b.createAccessor(d)}}},getAccessorKey:function(b){var a=this.getUseSimpleAccessors()?"simple":"";return this.$className+a+b},privates:{copyFrom:function(a){Ext.data.reader.Reader.prototype.copyFrom.call(this,a);this.getRoot=a.getRoot}}},0,0,0,0,["reader.json"],0,[Ext.data.reader,"Json",Ext.data,"JsonReader"],0));(Ext.cmd.derive("Ext.data.writer.Json",Ext.data.writer.Writer,{alternateClassName:"Ext.data.JsonWriter",config:{rootProperty:undefined,encode:false,allowSingle:true,expandData:false},getExpandedData:function(d){var b=d.length,e=0,k,a,g,c,h,l=function(i,j){var m={};m[i]=j;return m};for(;e0){h=k[a];for(;c>0;c--){h=l(g[c],h)}k[g[0]]=k[g[0]]||{};Ext.Object.merge(k[g[0]],h);delete k[a]}}}}return d},writeRecords:function(e,g){var d=this,a=d.getRootProperty(),c,h,b;if(d.getExpandData()){g=d.getExpandedData(g)}if(d.getAllowSingle()&&g.length===1){g=g[0];h=true}b=this.getTransform();if(b){g=b(g,e)}if(d.getEncode()){if(a){e.setParam(a,Ext.encode(g))}else{}}else{if(h||(g&&g.length)){c=e.getJsonData()||{};if(a){c[a]=g}else{c=g}e.setJsonData(c)}}return e}},0,0,0,0,["writer.json"],0,[Ext.data.writer,"Json",Ext.data,"JsonWriter"],0));(Ext.cmd.derive("Ext.util.Group",Ext.util.Collection,{config:{groupKey:null},$endUpdatePriority:2001},0,0,0,0,0,0,[Ext.util,"Group"],0));(Ext.cmd.derive("Ext.util.SorterCollection",Ext.util.Collection,{isSorterCollection:true,$sortable:null,sortFn:null,config:{sorterOptionsFn:null,sorterOptionsScope:null},constructor:function(a){var b=this;b.sortFn=Ext.util.Sorter.createComparator(b);Ext.util.Collection.prototype.constructor.call(this,a);b.setDecoder(b.decodeSorter)},addSort:function(j,i,c){var h=this,d,e,b,l,a,k,g;if(!j){h.beginUpdate();h.endUpdate()}else{l=h.getOptions();if(j instanceof Array){g=j;c=i;i=null}else{if(Ext.isString(j)){if(!(k=h.get(j))){g=[{property:j,direction:i||l.getDefaultSortDirection()}]}else{g=[k]}}else{if(Ext.isFunction(j)){g=[{sorterFn:j,direction:i||l.getDefaultSortDirection()}]}else{g=[j];c=i;i=null}}}c=h._sortModes[c||"replace"];a=h.getAt(0);d=h.length;e=c.append?d:0;h.beginUpdate();h.splice(e,c.replace?d:0,g);if(c.multi){d=h.length;b=l.getMultiSortLimit();if(d>b){h.removeAt(b,d)}}if(k&&i){k.setDirection(i)}else{if(e===0&&a&&a===h.getAt(0)){a.toggle()}}h.endUpdate()}},clear:function(){this.beginUpdate();Ext.util.Collection.prototype.clear.call(this);this.endUpdate(this.items)},getSortFn:function(){return this.sortFn},getByProperty:function(e){var b=this.items,a=b.length,c,d;for(c=0;cd+1||!Ext.isIterable(e)){e=Ext.Array.slice(h,d)}var l=k.items,g=e.length,c=[],b,m,a,o,j;for(b=0;b0;){m=l[a];if(m.getSorterFn()===o){c.push(m)}}}}}}e=c;e.$cloned=true}return e},getOptions:function(){return this.$sortable||this}},1,0,0,0,0,0,[Ext.util,"SorterCollection"],0));(Ext.cmd.derive("Ext.util.FilterCollection",Ext.util.Collection,{isFilterCollection:true,$filterable:null,filterFn:null,constructor:function(a){var b=this;b.filterFn=Ext.util.Filter.createFilterFn(b);Ext.util.Collection.prototype.constructor.call(this,a);b.setDecoder(b.decodeFilter)},filterData:function(a){return this.filtered?Ext.Array.filter(a,this.filterFn):a},getFilterFn:function(){return this.filterFn},isItemFiltered:function(a){return !this.filterFn(a)},decodeFilter:function(c){var b=this.getOptions(),a=b.getRootProperty(),d;if(c.isFilter){if(!c.getRoot()){c.setRoot(a)}}else{d={root:a};if(Ext.isFunction(c)){d.filterFn=c}else{d=Ext.apply(d,c);if(d.fn){d.filterFn=d.fn;delete d.fn}if(Ext.util.Filter.isInvalid(d)){return false}}c=new Ext.util.Filter(d)}return c},decodeRemoveItems:function(p,k){var r=this,l=(k===undefined)?p:p[k];if(!l.$cloned){if(p.length>k+1||!Ext.isIterable(l)){l=Ext.Array.slice(p,k)}var e=r.items,o=l.length,h=[],b,g,c,m,a,s,j,d,q;for(g=0;g0;){s=e[d];j=false;if(a){j=s.getProperty()===b}else{if(c){j=s.getFilterFn()===b}else{if(m){j=s.getProperty()===b.property&&s.getValue()===b.value}}}if(j){h.push(s)}}}}l=h;l.$cloned=true}return l},getOptions:function(){return this.$filterable||this}},1,0,0,0,0,0,[Ext.util,"FilterCollection"],0));(Ext.cmd.derive("Ext.util.GroupCollection",Ext.util.Collection,{isGroupCollection:true,config:{grouper:null,itemRoot:null},observerPriority:-100,onCollectionAdd:function(b,a){this.addItemsToGroups(b,a.items)},onCollectionBeforeItemChange:function(b,a){this.changeDetails=a},onCollectionBeginUpdate:function(){this.beginUpdate()},onCollectionEndUpdate:function(){this.endUpdate()},onCollectionItemChange:function(c,a){var b=a.item;if(!a.indexChanged){this.syncItemGrouping(c,b,c.getKey(b),a.oldKey,a.oldIndex)}this.changeDetails=null},onCollectionRefresh:function(a){this.removeAll();this.addItemsToGroups(a,a.items)},onCollectionRemove:function(a,b){var h=this,l=h.changeDetails,g,j,k,e,c,d,m;if(l){m=l.item;k=h.findGroupForItem(m);g=[];if(k){g.push({group:k,items:[m]})}}else{g=h.groupItems(a,b.items,false)}for(e=0,c=g.length;e0&&a.getSorters().getCount()===0){d=a.indexOf(o.items[0]);if(b-1){a=[b];g=1}else{g=0}}else{a=[];for(d=0,g=b.length;d=0;d--){b=k[d];b.reject();if(!h){j.insert(b.removedFrom||0,b)}}if(h){c.setAutoSort(e);j.add(k)}k.length=0}j.endUpdate();Ext.resumeLayouts(true)},onDestroy:function(){var b=this,a=b.loadTask,d=b.getData(),c=d.getSource();b.clearData();Ext.data.ProxyStore.prototype.onDestroy.call(this);b.setSession(null);b.observers=null;if(a){a.cancel();b.loadTask=null}if(c){c.destroy()}},privates:{fetch:function(b){b=Ext.apply({},b);this.setLoadOptions(b);var a=this.createOperation("read",b);a.execute()},onBeforeLoad:function(a){this.callObservers("BeforeLoad",[a])},onRemoteFilterSet:function(a,b){if(a){this.getData().setFilters(b?null:a)}Ext.data.ProxyStore.prototype.onRemoteFilterSet.call(this,a,b)},onRemoteSortSet:function(b,c){var a=this.getData();if(b){a.setSorters(c?null:b)}a.setAutoGroup(!c);Ext.data.ProxyStore.prototype.onRemoteSortSet.call(this,b,c)},isMoving:function(c,e){var g=this.moveMap,b=0,a,d;if(g){if(c){if(Ext.isArray(c)){for(d=0,a=c.length;d-1}}}return a}},1,0,0,0,0,0,[Ext.app.domain,"Store"],0));(Ext.cmd.derive("Ext.app.route.Queue",Ext.Base,{queue:null,token:null,constructor:function(a){Ext.apply(this,a);this.queue=new Ext.util.MixedCollection()},queueAction:function(a,b){this.queue.add({route:a,args:b})},clearQueue:function(){this.queue.removeAll()},runQueue:function(){var a=this.queue,c=a.removeAt(0),b;if(c){b=c&&c.route;b.execute(this.token,c.args,this.onActionExecute,this)}},onActionExecute:function(a){if(a){this.clearQueue()}else{this.runQueue()}}},1,0,0,0,0,0,[Ext.app.route,"Queue"],0));(Ext.cmd.derive("Ext.app.route.Route",Ext.Base,{action:null,conditions:null,controller:null,allowInactive:false,url:null,before:null,caseInsensitive:false,matcherRegex:null,paramMatchingRegex:null,paramsInMatchString:null,constructor:function(b){var c=this,a;Ext.apply(c,b,{conditions:{}});a=c.url;c.paramMatchingRegex=new RegExp(/:([0-9A-Za-z\_]*)/g);c.paramsInMatchString=a.match(c.paramMatchingRegex)||[];c.matcherRegex=c.createMatcherRegex(a)},recognize:function(c){var d=this,a=d.controller,e,b;if((d.allowInactive||a.isActive())&&d.recognizes(c)){e=d.matchesFor(c);b=c.match(d.matcherRegex);b.shift();return Ext.applyIf(e,{controller:a,action:d.action,historyUrl:c,args:b})}return false},recognizes:function(a){return this.matcherRegex.test(a)},execute:function(c,h,i,d){var b=h.args||[],g=this.before,a=this.controller,e=this.createCallback(h,i,d);if(g){b.push(e);if(Ext.isString(g)){g=this.before=a[g]}if(g){g.apply(a,b)}}else{e.resume()}},matchesFor:function(c){var g={},e=this.paramsInMatchString,b=c.match(this.matcherRegex),d=0,a=e.length;b.shift();for(;d0){a=c.substring(0,b);h=c.substring(b+1)+"."+a}else{if(c.indexOf(".")>0&&(Ext.ClassManager.isCreated(c)||this.hasRegisteredPrefix(c))){h=c}else{if(e){h=e+"."+g+"."+(d?d+"."+c:c);a=c}else{h=c}}}return{absoluteName:h,shortName:a}},hasRegisteredPrefix:function(a){var c=Ext.ClassManager,b=c.getPrefix(a);return b&&b!==a}},models:null,views:null,stores:null,controllers:null,config:{application:null,refs:null,active:true,moduleClassName:null},onClassExtended:function(b,c,a){var d=a.onBeforeCreated;a.onBeforeCreated=function(e,j){var k=Ext.app.Controller,h=[],g,i;i=e.prototype;g=k.resolveNamespace(e,j);if(g){i.$namespace=g}k.processDependencies(i,h,g,"model",j.models);k.processDependencies(i,h,g,"view",j.views);k.processDependencies(i,h,g,"store",j.stores);k.processDependencies(i,h,g,"controller",j.controllers);Ext.require(h,Ext.Function.pass(d,arguments,this))}},constructor:function(a){this.initAutoGetters();Ext.app.BaseController.prototype.constructor.apply(this,arguments)},normalizeRefs:function(b){var c=this,a=[];if(b){if(Ext.isObject(b)){Ext.Object.each(b,function(d,e){if(Ext.isString(e)){e={selector:e}}e.ref=d;a.push(e)})}else{if(Ext.isArray(b)){a=Ext.Array.merge(a,b)}}}b=c.refs;if(b){c.refs=null;b=c.normalizeRefs(b);if(b){a=Ext.Array.merge(a,b)}}return a},getRefMap:function(){var g=this,c=g._refMap,a,e,d,b;if(!c){a=g.getRefs();c=g._refMap={};if(a){for(b=0,d=a.length;b0){c=a[e];d.map[d.getKey(c)]=e}++d.generation}}},1,0,0,0,0,0,[Ext.util,"Bag"],0));(Ext.cmd.derive("Ext.util.Scheduler",Ext.Base,{busyCounter:0,lastBusyCounter:0,destroyed:false,firing:null,notifyIndex:-1,nextId:0,orderedItems:null,passes:0,scheduledCount:0,validIdRe:null,config:{cycleLimit:5,preSort:null,tickDelay:5},suspendOnNotify:true,constructor:function(a){this.mixins.observable.constructor.call(this,a);this.items=new Ext.util.Bag()},destroy:function(){var a=this,b=a.timer;if(b){window.clearTimeout(b);a.timer=null}a.items.destroy();a.items=a.orderedItems=null;a.callParent()},add:function(c){var b=this,a=b.items;if(a===b.firing){b.items=a=a.clone()}c.id=c.id||++b.nextId;c.scheduler=b;a.add(c);if(!b.sortMap){b.orderedItems=null}},remove:function(c){var b=this,a=b.items;if(b.destroyed){return}if(a===b.firing){b.items=a=a.clone()}if(c.scheduled){b.unscheduleItem(c);c.scheduled=false}a.remove(c);b.orderedItems=null},sort:function(){var d=this,a=d.items,e={},g=d.getPreSort(),b,c;d.orderedItems=[];d.sortMap=e;if(g){a.sort(g)}a=a.items;for(b=0;b0;){e[b].stub=c}}return c},isDescendantOf:function(b){for(var a=this;a=a.parent;){if(a===b){return true}}return false},onSchedule:function(){for(var b,a,d,e,c=this.parent;c;c=c.parent){e=c.bindings;if(e){for(b=0,a=e.length;b "+a.binding.getFullName()+")")},getDataObject:function(){var a=this.binding;return a&&a.getDataObject()},getRawValue:function(){var a=this.binding;return a&&a.getRawValue()},getValue:function(){var a=this.binding;return a&&a.getValue()},getTargetStub:function(){var a=this.binding;return a&&a.stub},isLoading:function(){var a=this.binding;return a?a.isLoading():false},link:function(d,b){var a=this,c=a.binding;if(c){c.destroy()}b=a.target=b||a.owner;a.linkDescriptor=d;a.binding=b.bind(d,a.onChange,a);a.binding.deep=true},onChange:function(){this.invalidate(true)},react:function(){var b=this,a=b.owner.linkData;a[b.name]=b.getValue();Ext.app.bind.Stub.prototype.react.call(this)},privates:{collect:function(){var b=this,a=Ext.app.bind.Stub.prototype.collect.call(this),c=b.binding?1:0;return a+c},sort:function(){var a=this.binding;if(a){this.scheduler.sortItem(a)}}}},0,0,0,0,0,0,[Ext.app.bind,"LinkStub"],0));(Ext.cmd.derive("Ext.app.bind.RootStub",Ext.app.bind.AbstractStub,{isRootStub:true,depth:0,createRootChild:function(a,e){var j=this,b=j.owner,g=b.getData(),c=j.children,i=c&&c[a],h=i?null:j,k,d;if(e||g.hasOwnProperty(a)||!(k=b.getParent())){d=new Ext.app.bind.Stub(b,a,h)}else{d=new Ext.app.bind.LinkStub(b,a,i?null:h);d.link("{"+a+"}",k)}if(i){i.graft(d)}return d},createStubChild:function(a){return this.createRootChild(a,true)},descend:function(g,c){var e=this,d=e.children,h=c||0,b=g[h++],a=(d&&d[b])||e.createRootChild(b);if(h0;){if(b[a].isLoading()){return true}}return false},isBindingStatic:function(a){return a.isTemplateBinding&&a.isStatic},isStatic:function(){var d=this.bindings,a=d.length,b,c;for(b=0;b-1}}}}return a}},1,0,0,0,0,0,[Ext.app.domain,"Controller"],0));(Ext.cmd.derive("Ext.direct.Manager",Ext.Base,{singleton:true,exceptions:{TRANSPORT:"xhr",PARSE:"parse",DATA:"data",LOGIN:"login",SERVER:"exception"},providerClasses:{},remotingMethods:{},config:{varName:"Ext.REMOTING_API"},apiNotFoundError:"Ext Direct API was not found at {0}",constructor:function(){var a=this;a.mixins.observable.constructor.call(a);a.transactions=new Ext.util.MixedCollection();a.providers=new Ext.util.MixedCollection()},addProvider:function(g){var d=this,b=arguments,e=d.relayers||(d.relayers={}),c,a;if(b.length>1){for(c=0,a=b.length;c0},connect:function(){var a=this;if(a.subscribers===0){a.doConnect();a.fireEventArgs("connect",[a])}a.subscribers++},doConnect:Ext.emptyFn,disconnect:function(b){var a=this;if(a.subscribers>0){if(b){a.subscribers=0}else{a.subscribers--}if(a.subscribers===0){a.doDisconnect();a.fireEventArgs("disconnect",[a])}}},doDisconnect:Ext.emptyFn,inheritableStatics:{checkConfig:Ext.returnFalse},onClassExtended:function(b,c,a){if(c.type){Ext.direct.Manager.addProviderClass(c.type,b)}}},1,0,0,0,["direct.provider"],[[Ext.mixin.Observable.prototype.mixinId||Ext.mixin.Observable.$className,Ext.mixin.Observable]],[Ext.direct,"Provider"],0));(Ext.cmd.derive("Ext.app.domain.Direct",Ext.app.EventDomain,{singleton:true,type:"direct",idProperty:"id",constructor:function(){var a=this;a.callParent();a.monitor(Ext.direct.Provider)}},1,0,0,0,0,0,[Ext.app.domain,"Direct"],0));(Ext.cmd.derive("Ext.data.PageMap",Ext.util.LruCache,{config:{store:null,pageSize:0,rootProperty:""},clear:function(a){var b=this;b.pageMapGeneration=(b.pageMapGeneration||0)+1;b.indexMap={};Ext.util.LruCache.prototype.clear.call(this,a)},forEach:function(l,n){var h=this,d=Ext.Object.getKeys(h.map),a=d.length,k=h.getPageSize(),c,b,m,g,e;for(c=0;cb},hasPage:function(a){return !!this.get(a)},peekPage:function(a){return this.map[a]},getAt:function(a){return this.getRange(a,a+1)[0]},getRange:function(a,b){b--;if(!this.hasRange(a,b)){Ext.raise("PageMap asked for range which it does not have")}var h=this,d=Ext.Array,i=h.getPageSize(),l=h.getPageFromRecordIndex(a),e=h.getPageFromRecordIndex(b),c=(l-1)*i,n=(e*i)-1,j=l,o=[],m,g,k;for(;j<=e;j++){if(j===l){m=a-c;k=m>0}else{m=0;k=false}if(j===e){g=i-(n-b);k=k||g=o){b=o-1;j=Math.max(b-i,0)}if(h.rangeCached(j,Math.min(b,h.totalCount))){h.loading=false;e.un("pageadd",a);c=e.getRange(j,b+1);h.fireEvent("load",h,c,true);h.fireEvent("refresh",h)}};l=Math.ceil((h.getLeadingBufferZone()+h.getTrailingBufferZone())/2);if(h.lastRequestStart&&h.preserveScrollOnReload){j=h.lastRequestStart;b=h.lastRequestEnd;k=h.getTotalCount()}else{j=n.start||0;b=j+(n.count||h.getPageSize())-1}e.clear(true);delete h.totalCount;j=Math.max(j-l,0);b=Math.min(b+l,k);g=h.getPageFromRecordIndex(j);m=h.getPageFromRecordIndex(b);h.loading=true;n.waitForReload=a;e.on("pageadd",a);for(d=g;d<=m;d++){h.prefetchPage(d,n)}},filter:function(){Ext.data.ProxyStore.prototype.filter.apply(this,arguments)},filterBy:function(b,a){},loadData:function(b,a){},loadPage:function(c,a){var b=this;a=a||{};a.page=b.currentPage=c;a.start=(c-1)*b.getPageSize();a.limit=b.getViewSize()||b.getDefaultViewSize();a.loadCallback=a.callback;a.callback=null;return b.loadToPrefetch(a)},clearData:function(c){var a=this,b=a.getData();if(b){b.clear()}},getCount:function(){return this.totalCount||0},getRange:function(d,h,m){var l=this,e=l.totalCount-1,g=l.lastRequestStart,n=[],i=l.getData(),c,k,b,a,j;m=Ext.apply({prefetchStart:d,prefetchEnd:h},m);h=(h>=l.totalCount)?e:h;k=d===0?0:d-1;b=h===e?h:h+1;l.lastRequestStart=d;l.lastRequestEnd=h;if(l.rangeCached(k,b)){l.onRangeAvailable(m);n=i.getRange(d,h+1)}else{l.fireEvent("cachemiss",l,d,h);a=l.getPageFromRecordIndex(k);j=l.getPageFromRecordIndex(b);c=function(o,q,p){if(q>=a&&q<=j&&l.rangeCached(k,b)){l.fireEvent("cachefilled",l,d,h);i.un("pageadd",c);l.onRangeAvailable(m)}};i.on("pageadd",c);l.prefetchRange(d,h)}l.primeCache(d,h,d-1},indexOf:function(a){return this.getData().indexOf(a)},indexOfId:function(a){return this.indexOf(this.getById(a))},group:function(b,d){var c=this,a;if(b&&typeof b==="string"){a=c.grouper;if(!a){c.grouper=new Ext.util.Grouper({property:b,direction:d||"ASC",root:"data"})}else{if(d===undefined){a.toggle()}else{a.setDirection(d)}}}else{c.grouper=b?c.getSorters().decodeSorter(b,"Ext.util.Grouper"):null}c.getData().clear();c.loadPage(1,{callback:function(){c.fireEvent("groupchange",c,c.getGrouper())}})},getPageFromRecordIndex:function(a){return Math.floor(a/this.getPageSize())+1},calculatePageCacheSize:function(a){var c=this,b=c.getPurgePageCount();return b?Math.max(c.getData().getMaxSize()||0,Math.ceil((a+c.getTrailingBufferZone()+c.getLeadingBufferZone())/c.getPageSize())*2+b):0},loadToPrefetch:function(s){var m=this,c=s,h,b,o,n=s.start,a=s.start+s.limit-1,r=(m.getViewSize()||s.limit),j=Math.min(a,s.start+r-1),k=m.getPageFromRecordIndex(Math.max(n-m.getTrailingBufferZone(),0)),q=m.getPageFromRecordIndex(a+m.getLeadingBufferZone()),g=m.getData(),l=function(){b=b||[];if(s.loadCallback){s.loadCallback.call(s.scope||m,b,e,true)}if(s.callback){s.callback.call(s.scope||m,b,n||0,a||0,s)}},p=function(){m.fireEvent("datachanged",m);m.fireEvent("refresh",m);m.fireEvent("load",m,b,true)},d=function(){if(m.rangeCached(n,j)){m.loading=false;b=g.getRange(n,j+1);g.un("pageadd",d);if(m.hasListeners.guaranteedrange){m.guaranteeRange(n,j,s.callback,s.scope)}l();p()}},e;g.setMaxSize(m.calculatePageCacheSize(r));if(m.fireEvent("beforeload",m,s)!==false){delete m.totalCount;m.loading=true;if(s.callback){c=Ext.apply({},s);delete c.callback}m.on("prefetch",function(t,i,v,u){e=u;if(v){if((o=m.getTotalCount())){g.on("pageadd",d);j=Math.min(j,o-1);q=m.getPageFromRecordIndex(Math.min(j+m.getLeadingBufferZone(),o-1));for(h=k+1;h<=q;++h){m.prefetchPage(h,c)}}else{l();p()}}else{m.loading=false;l();m.fireEvent("load",m,i,false)}},null,{single:true});m.prefetchPage(k,c)}},prefetch:function(d){var e=this,b=e.getPageSize(),g=e.getData(),c,a;if(b){if(e.lastPageSize&&b!=e.lastPageSize){Ext.raise("pageSize cannot be dynamically altered")}if(!g.getPageSize()){g.setPageSize(b)}}else{e.pageSize=g.setPageSize(b=d.limit)}e.lastPageSize=b;if(!d.page){d.page=e.getPageFromRecordIndex(d.start);d.start=(d.page-1)*b;d.limit=Math.ceil(d.limit/b)*b}a=e.pageRequests[d.page];if(!a||a.getOperation().pageMapGeneration!==g.pageMapGeneration){d=Ext.apply({action:"read",filters:e.getFilters().items,sorters:e.getSorters().items,grouper:e.getGrouper(),internalCallback:e.onProxyPrefetch,internalScope:e},d);c=e.createOperation("read",d);c.pageMapGeneration=g.pageMapGeneration;if(e.fireEvent("beforeprefetch",e,c)!==false){e.pageRequests[d.page]=c.execute();if(e.getProxy().isSynchronous){delete e.pageRequests[d.page]}}}return e},onPageMapClear:function(){var c=this,b=c.wasLoading,a=c.pageRequests,e=c.getData(),d;e.clearListeners();e.on("clear",c.onPageMapClear,c);c.relayEvents(e,["beforepageremove","pageadd","pageremove"]);c.loading=true;c.totalCount=0;for(d in a){if(a.hasOwnProperty(d)){a[d].getOperation().abort()}}c.fireEvent("clear",c);c.loading=b},prefetchPage:function(e,b){var d=this,a=d.getPageSize(),g=(e-1)*a,c=d.totalCount;if(c!==undefined&&d.data.getCount()===c){return}d.prefetch(Ext.applyIf({page:e,start:g,limit:a},b))},onProxyPrefetch:function(d){if(this.destroyed){return}var i=this,j=d.getResultSet(),c=d.getRecords(),g=d.wasSuccessful(),h=d.getPage(),b=d.waitForReload,l=i.totalCount,a=i.pageRequests,k,e;if(d.pageMapGeneration===i.getData().pageMapGeneration){if(j){i.totalCount=j.getTotal();if(i.totalCount!==l){i.fireEvent("totalcountchange",i.totalCount)}}if(h!==undefined){delete i.pageRequests[h]}i.loading=false;i.fireEvent("prefetch",i,c,g,d);if(g){if(i.totalCount===0){if(b){for(k in a){e=a[k].getOperation();if(e.waitForReload===b){delete e.waitForReload}}i.getData().un("pageadd",b);i.fireEvent("load",i,[],true);i.fireEvent("refresh",i)}}else{i.cachePage(c,d.getPage())}}Ext.callback(d.getCallback(),d.getScope()||i,[c,d,g])}},cachePage:function(b,e){var d=this,a=b.length,c;if(!Ext.isDefined(d.totalCount)){d.totalCount=b.length;d.fireEvent("totalcountchange",d.totalCount)}for(c=0;cb-1)?b-1:d.prefetchEnd,c;a=Math.max(0,a);c=e.getData().getRange(g,a+1);if(d.fireEvent!==false){e.fireEvent("guaranteedrange",c,g,a,d)}if(d.callback){d.callback.call(d.scope||e,c,g,a,d)}},guaranteeRange:function(e,a,d,c,b){b=Ext.apply({callback:d,scope:c},b);this.getRange(e,a+1,b)},prefetchRange:function(h,b){var d=this,c,a,g,e=d.getData();if(!d.rangeCached(h,b)){c=d.getPageFromRecordIndex(h);a=d.getPageFromRecordIndex(b);e.setMaxSize(d.calculatePageCacheSize(b-h+1));for(g=c;g<=a;g++){if(!d.pageCached(g)){d.prefetchPage(g)}}}},primeCache:function(i,c,h){var g=this,e=g.getLeadingBufferZone(),d=g.getTrailingBufferZone(),b=g.getPageSize(),a=g.totalCount;if(h===-1){i=Math.max(i-e,0);c=Math.min(c+d,a-1)}else{if(h===1){i=Math.max(Math.min(i-d,a-b),0);c=Math.min(c+e,a-1)}else{i=Math.min(Math.max(Math.floor(i-((e+d)/2)),0),a-g.pageSize);c=Math.min(Math.max(Math.ceil(c+((e+d)/2)),0),a-1)}}g.prefetchRange(i,c)},sort:function(b,a,c){if(arguments.length===0){this.clearAndLoad()}else{this.getSorters().addSort(b,a,c)}},onSorterEndUpdate:function(){var a=this,b=a.getSorters().getRange();if(b.length){a.fireEvent("beforesort",a,b);a.clearAndLoad({callback:function(){a.fireEvent("sort",a,b)}})}else{a.fireEvent("sort",a,b)}},clearAndLoad:function(a){this.getData().clear();this.loadPage(1,a)},privates:{isLast:function(a){return this.indexOf(a)===this.getTotalCount()-1},isMoving:function(){return false}}},0,0,0,0,["store.buffered"],0,[Ext.data,"BufferedStore"],0));(Ext.cmd.derive("Ext.data.proxy.Direct",Ext.data.proxy.Server,{alternateClassName:"Ext.data.DirectProxy",config:{paramOrder:undefined,paramsAsHash:true,directFn:undefined,api:undefined,metadata:undefined},paramOrderRe:/[\s,|]/,applyParamOrder:function(a){if(Ext.isString(a)){a=a.split(this.paramOrderRe)}return a},updateApi:function(){this.methodsResolved=false},updateDirectFn:function(){this.methodsResolved=false},resolveMethods:function(){var d=this,c=d.getDirectFn(),b=d.getApi(),a=Ext.direct.Manager,e;if(c){d.setDirectFn(e=a.parseMethod(c));if(!Ext.isFunction(e)){Ext.raise("Cannot resolve directFn "+c)}}if(b){for(c in b){if(b.hasOwnProperty(c)){e=b[c];b[c]=a.parseMethod(e);if(!Ext.isFunction(b[c])){Ext.raise("Cannot resolve Direct api "+c+" method "+e)}}}}d.methodsResolved=true},doRequest:function(d){var i=this,a,e,c,b,h,g,j,k;if(!i.methodsResolved){i.resolveMethods()}e=i.buildRequest(d);c=e.getAction();g=i.getApi();if(g){j=g[c]}j=j||i.getDirectFn();a=i.getWriter();if(a&&d.allowWrite()){e=a.write(e)}if(c==="read"){b=e.getParams()}else{b=e.getJsonData()}h=j.directCfg.method.getArgs({params:b,paramOrder:i.getParamOrder(),paramsAsHash:i.getParamsAsHash(),metadata:i.getMetadata(),callback:i.createRequestCallback(e,d),scope:i});e.setConfig({args:h,directFn:j});j.apply(window,h);return e},applyEncoding:Ext.identityFn,createRequestCallback:function(c,a){var b=this;return function(e,d){b.processResponse(d.status,a,c,d)}},extractResponseData:function(a){return Ext.isDefined(a.result)?a.result:a.data},setException:function(b,a){b.setException(a.message)},buildUrl:function(){return""}},0,0,0,0,["proxy.direct"],0,[Ext.data.proxy,"Direct",Ext.data,"DirectProxy"],0));(Ext.cmd.derive("Ext.data.DirectStore",Ext.data.Store,{constructor:function(a){a=Ext.apply({},a);if(!a.proxy){var b={type:"direct",reader:{type:"json"}};Ext.copyTo(b,a,"paramOrder,paramsAsHash,directFn,api,simpleSortMode,extraParams");Ext.copyTo(b.reader,a,"totalProperty,root,rootProperty,idProperty");a.proxy=b}Ext.data.Store.prototype.constructor.call(this,a)}},1,0,0,0,["store.direct"],0,[Ext.data,"DirectStore"],0));(Ext.cmd.derive("Ext.data.JsonP",Ext.Base,{singleton:true,requestCount:0,requests:{},timeout:30000,disableCaching:true,disableCachingParam:"_dc",callbackKey:"callback",request:function(n){n=Ext.apply({},n);var j=this,d=Ext.isDefined(n.disableCaching)?n.disableCaching:j.disableCaching,h=n.disableCachingParam||j.disableCachingParam,c=++j.requestCount,l=n.callbackName||"callback"+c,i=n.callbackKey||j.callbackKey,m=Ext.isDefined(n.timeout)?n.timeout:j.timeout,e=Ext.apply({},n.params),b=n.url,a=Ext.name,g,k;if(d&&!e[h]){e[h]=Ext.Date.now()}n.params=e;e[i]=a+".data.JsonP."+l;k=j.createScript(b,e,n);j.requests[c]=g={url:b,params:e,script:k,id:c,scope:n.scope,success:n.success,failure:n.failure,callback:n.callback,callbackKey:i,callbackName:l};if(m>0){g.timeout=Ext.defer(j.handleTimeout,m,j,[g])}j.setupErrorHandling(g);j[l]=Ext.bind(j.handleResponse,j,[g],true);j.loadScript(g);return g},abort:function(c){var b=this,d=b.requests,a;if(c){if(!c.id){c=d[c]}b.handleAbort(c)}else{for(a in d){if(d.hasOwnProperty(a)){b.abort(d[a])}}}},setupErrorHandling:function(a){a.script.onerror=Ext.bind(this.handleError,this,[a])},handleAbort:function(a){a.errorType="abort";this.handleResponse(null,a)},handleError:function(a){a.errorType="error";this.handleResponse(null,a)},cleanupErrorHandling:function(a){a.script.onerror=null},handleTimeout:function(a){a.errorType="timeout";this.handleResponse(null,a)},handleResponse:function(b,c){var d=true,a=Ext.GlobalEvents;if(c.timeout){clearTimeout(c.timeout)}delete this[c.callbackName];delete this.requests[c.id];this.cleanupErrorHandling(c);Ext.fly(c.script).destroy();if(c.errorType){d=false;Ext.callback(c.failure,c.scope,[c.errorType])}else{Ext.callback(c.success,c.scope,[b])}Ext.callback(c.callback,c.scope,[d,b,c.errorType]);if(a.hasListeners.idle){a.fireEvent("idle")}},createScript:function(c,d,b){var a=document.createElement("script");a.setAttribute("src",Ext.urlAppend(c,Ext.Object.toQueryString(d)));a.setAttribute("async",true);a.setAttribute("type","text/javascript");return a},loadScript:function(a){Ext.getHead().appendChild(a.script)}},0,0,0,0,0,0,[Ext.data,"JsonP"],0));(Ext.cmd.derive("Ext.data.proxy.JsonP",Ext.data.proxy.Server,{alternateClassName:"Ext.data.ScriptTagProxy",config:{callbackKey:"callback",recordParam:"records",autoAppendParams:true},doRequest:function(a){var c=this,b=c.buildRequest(a),d=b.getParams();b.setConfig({callbackKey:c.callbackKey,timeout:c.timeout,scope:c,disableCaching:false,callback:c.createRequestCallback(b,a)});if(c.getAutoAppendParams()){b.setParams({})}b.setRawRequest(Ext.data.JsonP.request(b.getCurrentConfig()));b.setParams(d);c.lastRequest=b;return b},createRequestCallback:function(c,a){var b=this;return function(g,d,e){if(c===b.lastRequest){b.lastRequest=null}b.processResponse(g,a,c,d)}},setException:function(b,a){b.setException(b.getRequest().getRawRequest().errorType)},buildUrl:function(h){var k=this,a=Ext.data.proxy.Server.prototype.buildUrl.apply(this,arguments),d=h.getRecords(),e=k.getWriter(),g,c,b,j,l;if(e&&h.getOperation().allowWrite()){h=e.write(h)}g=h.getParams();c=g.filters;delete g.filters;if(c&&c.length){for(j=0;j0&&(!e||!e.getEncode())){g[k.getRecordParam()]=k.encodeRecords(d)}if(k.getAutoAppendParams()){a=Ext.urlAppend(a,Ext.Object.toQueryString(g))}return a},abort:function(a){a=a||this.lastRequest;if(a){Ext.data.JsonP.abort(a.getRawRequest())}},encodeRecords:function(b){var d=[],c=0,a=b.length;for(;c0},isExpandable:function(){var c=this;if(c.get("expandable")){return !(c.isLeaf()||(c.isLoaded()&&!c.phantom&&!c.hasChildNodes()))}return false},triggerUIUpdate:function(){this.callJoined("afterEdit",[])},appendChild:function(g,o,h){var m=this,j,l,k,n,c,q={isLast:true,parentId:m.getId(),depth:(m.data.depth||0)+1},p,e=m.getTreeStore(),d=e&&e.bulkUpdate;Ext.suspendLayouts();if(Ext.isArray(g)){l=g.length;p=new Array(l);m.callTreeStore("beginFill");for(j=0;j0){if(!m){m=j.getTreeStore().getSortFn()}Ext.Array.sort(k,m);j.setFirstChild(k[0]);j.setLastChild(k[h-1]);for(g=0;g0){d.children=h=[];for(g=0;g1&&!g.getRemoteSort()&&g.getFolderSort()||e.length,l,j,d,k;if(g.needsLocalFilter()){k=g.getFilters().getFilterFn();c[0].set("visible",k(c[0]))}for(d=1;d")}else{k.push(">");if((j=q.tpl)){j.applyOut(q.tplData,k)}if((j=q.html)){k.push(j)}if((j=q.cn||q.children)){p.generateMarkup(j,k)}l=p.closeTags;k.push(l[r]||(l[r]=""))}}}return k},generateStyles:function(n,k,m){var j=k||[],l,o;for(l in n){if(n.hasOwnProperty(l)){o=n[l];l=this.decamelizeName(l);if(m&&Ext.String.hasHtmlCharacters(o)){o=Ext.String.htmlEncode(o)}j.push(l,":",o,";")}}return k||j.join("")},markup:function(j){if(typeof j==="string"){return j}var k=this.generateMarkup(j,[]);return k.join("")},applyStyles:function(j,k){Ext.fly(j).applyStyles(k)},createContextualFragment:function(k){var n=this.detachedDiv,j=document.createDocumentFragment(),l,m;n.innerHTML=k;m=n.childNodes;l=m.length;while(l--){j.appendChild(m[0])}return j},createDom:function(m,j){var l=this,k=l.markup(m),p=l.detachedDiv,n;p.innerHTML=k;n=p.firstChild;return Ext.supports.ChildContentClearedWhenSettingInnerHTML?n.cloneNode(true):n},insertHtml:function(m,j,n){var r=this,k,o,l,q,s;m=m.toLowerCase();if(j.insertAdjacentHTML){if(r.ieInsertHtml){s=r.ieInsertHtml(m,j,n);if(s){return s}}k=b[m];if(k){j.insertAdjacentHTML(k[0],n);return j[k[1]]}}else{if(j.nodeType===3){m=m===a?h:m;m=m===d?g:m}o=Ext.supports.CreateContextualFragment?j.ownerDocument.createRange():undefined;q="setStart"+(this.endRe.test(m)?"After":"Before");if(c[m]){if(o){o[q](j);s=o.createContextualFragment(n)}else{s=this.createContextualFragment(n)}j.parentNode.insertBefore(s,m===h?j:j.nextSibling);return j[(m===h?"previous":"next")+"Sibling"]}else{l=(m===a?"first":"last")+"Child";if(j.firstChild){if(o){try{o[q](j[l]);s=o.createContextualFragment(n)}catch(p){s=this.createContextualFragment(n)}}else{s=this.createContextualFragment(n)}if(m===a){j.insertBefore(s,j.firstChild)}else{j.appendChild(s)}}else{j.innerHTML=n}return j[l]}}},insertBefore:function(j,l,k){return this.doInsert(j,l,k,h)},insertAfter:function(j,l,k){return this.doInsert(j,l,k,g)},insertFirst:function(j,l,k){return this.doInsert(j,l,k,a)},append:function(j,l,k){return this.doInsert(j,l,k,d)},overwrite:function(l,k,n){var m=this,j;l=Ext.getDom(l);k=m.markup(k);if(m.ieOverwrite){j=m.ieOverwrite(l,k)}if(!j){l.innerHTML=k;j=l.firstChild}return n?Ext.get(j):j},doInsert:function(l,p,n,j){var m=this,k;l=l.dom||Ext.getDom(l);if("innerHTML" in l){k=m.insertHtml(j,l,m.markup(p))}else{k=m.createDom(p,null);if(l.nodeType===3){j=j===a?h:j;j=j===d?g:j}if(c[j]){l.parentNode.insertBefore(k,j===h?l:l.nextSibling)}else{if(l.firstChild&&j===a){l.insertBefore(k,l.firstChild)}else{l.appendChild(k)}}}return n?Ext.get(k):k},createTemplate:function(k){var j=this.markup(k);return new Ext.Template(j)},createHtml:function(j){return this.markup(j)}}},0,0,0,0,0,0,[Ext.dom,"Helper",Ext,"DomHelper",Ext.core,"DomHelper"],0));Ext.define("Ext.overrides.dom.Helper",(function(){var c=/^(?:table|thead|tbody|tr|td)$/i,h=/td|tr|tbody|thead/i,g="",i="
    ",b=g+"",e=""+i,a=b+"",d=""+e;return{override:"Ext.dom.Helper",ieInsertHtml:function(j,l,k){var m=null;if(Ext.isIE9m&&c.test(l.tagName)){m=this.insertIntoTable(l.tagName.toLowerCase(),j,l,k)}return m},ieOverwrite:function(k,j){if(Ext.isIE9m&&c.test(k.tagName)){while(k.firstChild){k.removeChild(k.firstChild)}if(j){return this.insertHtml("afterbegin",k,j)}}},ieTable:function(p,k,q,o){var l=-1,n=this.detachedDiv,m,j;n.innerHTML=[k,q,o].join("");while(++ltext";return d.children&&(d.children.length===0)})(),nonSpace=/\S/,trimRe=/^\s+|\s+$/g,tplRe=/\{(\d+)\}/g,modeRe=/^(\s?[\/>+~]\s?|\s|$)/,tagTokenRe=/^(#)?([\w\-\*\|\\]+)/,nthRe=/(\d*)n\+?(\d*)/,nthRe2=/\D/,startIdRe=/^\s*#/,isIE=window.ActiveXObject?true:false,key=30803,longHex=/\\([0-9a-fA-F]{6})/g,shortHex=/\\([0-9a-fA-F]{1,6})\s{0,1}/g,nonHex=/\\([^0-9a-fA-F]{1})/g,escapes=/\\/g,num,hasEscapes,supportsColonNsSeparator=(function(){var xmlDoc,xmlString='';if(window.DOMParser){xmlDoc=(new DOMParser()).parseFromString(xmlString,"application/xml")}else{xmlDoc=new ActiveXObject("Microsoft.XMLDOM");xmlDoc.loadXML(xmlString)}return !!xmlDoc.getElementsByTagName("a:b").length})(),longHexToChar=function($0,$1){return String.fromCharCode(parseInt($1,16))},shortToLongHex=function($0,$1){while($1.length<6){$1="0"+$1}return"\\"+$1},charToLongHex=function($0,$1){num=$1.charCodeAt(0).toString(16);if(num.length===1){num="0"+num}return"\\0000"+num},unescapeCssSelector=function(selector){return(hasEscapes)?selector.replace(longHex,longHexToChar):selector},setupEscapes=function(path){hasEscapes=(path.indexOf("\\")>-1);if(hasEscapes){path=path.replace(shortHex,shortToLongHex).replace(nonHex,charToLongHex).replace(escapes,"\\\\")}return path};eval("var batch = 30803, child, next, prev, byClassName;");child=useChildrenCollection?function child(parent,index){return parent.children[index]}:function child(parent,index){var i=0,n=parent.firstChild;while(n){if(n.nodeType==1){if(++i==index){return n}}n=n.nextSibling}return null};next=useElementPointer?function(n){return n.nextElementSibling}:function(n){while((n=n.nextSibling)&&n.nodeType!=1){}return n};prev=useElementPointer?function(n){return n.previousElementSibling}:function(n){while((n=n.previousSibling)&&n.nodeType!=1){}return n};function children(parent){var n=parent.firstChild,nodeIndex=-1,nextNode;while(n){nextNode=n.nextSibling;if(n.nodeType==3&&!nonSpace.test(n.nodeValue)){parent.removeChild(n)}else{n.nodeIndex=++nodeIndex}n=nextNode}return this}byClassName=useClassList?function(nodeSet,cls){cls=unescapeCssSelector(cls);if(!cls){return nodeSet}var result=[],ri=-1,i,ci,classList;for(i=0;ci=nodeSet[i];i++){classList=ci.classList;if(classList){if(classList.contains(cls)){result[++ri]=ci}}else{if((" "+ci.className+" ").indexOf(cls)!==-1){result[++ri]=ci}}}return result}:function(nodeSet,cls){cls=unescapeCssSelector(cls);if(!cls){return nodeSet}var result=[],ri=-1,i,ci;for(i=0;ci=nodeSet[i];i++){if((" "+ci.className+" ").indexOf(cls)!==-1){result[++ri]=ci}}return result};function attrValue(n,attr){if(!n.tagName&&typeof n.length!="undefined"){n=n[0]}if(!n){return null}if(attr=="for"){return n.htmlFor}if(attr=="class"||attr=="className"){return n.className}return n.getAttribute(attr)||n[attr]}function getNodes(ns,mode,tagName){var result=[],ri=-1,cs,i,ni,j,ci,cn,utag,n,cj;if(!ns){return result}tagName=tagName.replace("|",":")||"*";if(typeof ns.getElementsByTagName!="undefined"){ns=[ns]}if(!mode){tagName=unescapeCssSelector(tagName);if(!supportsColonNsSeparator&&DQ.isXml(ns[0])&&tagName.indexOf(":")!==-1){for(i=0;ni=ns[i];i++){cs=ni.getElementsByTagName(tagName.split(":").pop());for(j=0;ci=cs[j];j++){if(ci.tagName===tagName){result[++ri]=ci}}}}else{for(i=0;ni=ns[i];i++){cs=ni.getElementsByTagName(tagName);for(j=0;ci=cs[j];j++){result[++ri]=ci}}}}else{if(mode=="/"||mode==">"){utag=tagName.toUpperCase();for(i=0;ni=ns[i];i++){cn=ni.childNodes;for(j=0;cj=cn[j];j++){if(cj.nodeName==utag||cj.nodeName==tagName||tagName=="*"){result[++ri]=cj}}}}else{if(mode=="+"){utag=tagName.toUpperCase();for(i=0;n=ns[i];i++){while((n=n.nextSibling)&&n.nodeType!=1){}if(n&&(n.nodeName==utag||n.nodeName==tagName||tagName=="*")){result[++ri]=n}}}else{if(mode=="~"){utag=tagName.toUpperCase();for(i=0;n=ns[i];i++){while((n=n.nextSibling)){if(n.nodeName==utag||n.nodeName==tagName||tagName=="*"){result[++ri]=n}}}}}}}return result}function concat(a,b){a.push.apply(a,b);return a}function byTag(cs,tagName){if(cs.tagName||cs===doc){cs=[cs]}if(!tagName){return cs}var result=[],ri=-1,i,ci;tagName=tagName.toLowerCase();for(i=0;ci=cs[i];i++){if(ci.nodeType==1&&ci.tagName.toLowerCase()==tagName){result[++ri]=ci}}return result}function byId(cs,id){id=unescapeCssSelector(id);if(cs.tagName||cs===doc){cs=[cs]}if(!id){return cs}var result=[],ri=-1,i,ci;for(i=0;ci=cs[i];i++){if(ci&&ci.id==id){result[++ri]=ci;return result}}return result}function byAttribute(cs,attr,value,op,custom){var result=[],ri=-1,useGetStyle=custom=="{",fn=DQ.operators[op],a,xml,hasXml,i,ci;value=unescapeCssSelector(value);for(i=0;ci=cs[i];i++){if(ci.nodeType===1){if(!hasXml){xml=DQ.isXml(ci);hasXml=true}if(!xml){if(useGetStyle){a=DQ.getStyle(ci,attr)}else{if(attr=="class"||attr=="className"){a=ci.className}else{if(attr=="for"){a=ci.htmlFor}else{if(attr=="href"){a=ci.getAttribute("href",2)}else{a=ci.getAttribute(attr)}}}}}else{a=ci.getAttribute(attr)}if((fn&&fn(a,value))||(!fn&&a)){result[++ri]=ci}}}return result}function byPseudo(cs,name,value){value=unescapeCssSelector(value);return DQ.pseudos[name](cs,value)}function nodupIEXml(cs){var d=++key,r,i,len,c;cs[0].setAttribute("_nodup",d);r=[cs[0]];for(i=1,len=cs.length;i1){return nodup(results)}return results},isXml:function(el){var docEl=(el?el.ownerDocument||el:0).documentElement;return docEl?docEl.nodeName!=="HTML":false},select:doc.querySelectorAll?function(path,root,type,single){root=root||doc;if(!DQ.isXml(root)){try{if(root.parentNode&&(root.nodeType!==9)&&path.indexOf(",")===-1&&!startIdRe.test(path)){path=Ext.makeIdSelector(Ext.id(root))+" "+path;root=root.parentNode}return single?[root.querySelector(path)]:Ext.Array.toArray(root.querySelectorAll(path))}catch(e){}}return DQ.jsSelect.call(this,path,root,type)}:function(path,root,type){return DQ.jsSelect.call(this,path,root,type)},selectNode:function(path,root){return Ext.DomQuery.select(path,root,null,true)[0]},selectValue:function(path,root,defaultValue){path=path.replace(trimRe,"");var query=valueCache.get(path),n,v;if(!query){query=DQ.compile(path,"select");valueCache.add(path,query)}else{setupEscapes(path)}n=query(root);return DQ.getNodeValue(n[0]||n,defaultValue)},getNodeValue:function(node,defaultValue){if(typeof node.normalize=="function"){node.normalize()}var firstChild=node&&node.firstChild,v=firstChild?firstChild.nodeValue:null;if(defaultValue!==undefined&&(v==null||v==="")){v=defaultValue}return v},selectNumber:function(path,root,defaultValue){var v=DQ.selectValue(path,root,defaultValue||0);return parseFloat(v)},is:function(el,ss){if(typeof el=="string"){el=doc.getElementById(el)}var isArray=Ext.isArray(el),result=DQ.filter(isArray?el:[el],ss);return isArray?(result.length==el.length):(result.length>0)},filter:function(els,ss,nonMatches){ss=ss.replace(trimRe,"");var query=simpleCache.get(ss),result;if(!query){query=DQ.compile(ss,"simple");simpleCache.add(ss,query)}else{setupEscapes(ss)}result=query(els);return nonMatches?quickDiff(result,els):result},matchers:[{re:/^\.([\w\-\\]+)/,select:useClassList?'n = byClassName(n, "{1}");':'n = byClassName(n, " {1} ");'},{re:/^\:([\w\-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,select:'n = byPseudo(n, "{1}", "{2}");'},{re:/^(?:([\[\{])(?:@)?([\w\-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,select:'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'},{re:/^#([\w\-\\]+)/,select:'n = byId(n, "{1}");'},{re:/^@([\w\-\.]+)/,select:'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'}],pseudos:{"first-child":function(c){var r=[],ri=-1,n,i,ci;for(i=0;(ci=n=c[i]);i++){while((n=n.previousSibling)&&n.nodeType!=1){}if(!n){r[++ri]=ci}}return r},"last-child":function(c){var r=[],ri=-1,n,i,ci;for(i=0;(ci=n=c[i]);i++){while((n=n.nextSibling)&&n.nodeType!=1){}if(!n){r[++ri]=ci}}return r},"nth-child":function(c,a){var r=[],ri=-1,m=nthRe.exec(a=="even"&&"2n"||a=="odd"&&"2n+1"||!nthRe2.test(a)&&"n+"+a||a),f=(m[1]||1)-0,l=m[2]-0,i,n,j,cn,pn;for(i=0;n=c[i];i++){pn=n.parentNode;if(batch!=pn._batch){j=0;for(cn=pn.firstChild;cn;cn=cn.nextSibling){if(cn.nodeType==1){cn.nodeIndex=++j}}pn._batch=batch}if(f==1){if(l===0||n.nodeIndex==l){r[++ri]=n}}else{if((n.nodeIndex+l)%f===0){r[++ri]=n}}}return r},"only-child":function(c){var r=[],ri=-1,i,ci;for(i=0;ci=c[i];i++){if(!prev(ci)&&!next(ci)){r[++ri]=ci}}return r},empty:function(c){var r=[],ri=-1,i,ci,cns,j,cn,empty;for(i=0;ci=c[i];i++){cns=ci.childNodes;j=0;empty=true;while(cn=cns[j]){++j;if(cn.nodeType==1||cn.nodeType==3){empty=false;break}}if(empty){r[++ri]=ci}}return r},contains:function(c,v){var r=[],ri=-1,i,ci;for(i=0;ci=c[i];i++){if((ci.textContent||ci.innerText||ci.text||"").indexOf(v)!=-1){r[++ri]=ci}}return r},nodeValue:function(c,v){var r=[],ri=-1,i,ci;for(i=0;ci=c[i];i++){if(ci.firstChild&&ci.firstChild.nodeValue==v){r[++ri]=ci}}return r},checked:function(c){var r=[],ri=-1,i,ci;for(i=0;ci=c[i];i++){if(ci.checked===true){r[++ri]=ci}}return r},not:function(c,ss){return DQ.filter(c,ss,true)},any:function(c,selectors){var ss=selectors.split("|"),r=[],ri=-1,s,i,ci,j;for(i=0;ci=c[i];i++){for(j=0;s=ss[j];j++){if(DQ.is(ci,s)){r[++ri]=ci;break}}}return r},odd:function(c){return this["nth-child"](c,"odd")},even:function(c){return this["nth-child"](c,"even")},nth:function(c,a){return c[a-1]||[]},first:function(c){return c[0]||[]},last:function(c){return c[c.length-1]||[]},has:function(c,ss){var s=DQ.select,r=[],ri=-1,i,ci;for(i=0;ci=c[i];i++){if(s(ss,ci).length>0){r[++ri]=ci}}return r},next:function(c,ss){var is=DQ.is,r=[],ri=-1,i,ci,n;for(i=0;ci=c[i];i++){n=next(ci);if(n&&is(n,ss)){r[++ri]=ci}}return r},prev:function(c,ss){var is=DQ.is,r=[],ri=-1,i,ci,n;for(i=0;ci=c[i];i++){n=prev(ci);if(n&&is(n,ss)){r[++ri]=ci}}return r},focusable:function(candidates){var len=candidates.length,results=[],i=0,c;for(;i0||s.left>0){r[++ri]=ci}}return r}}}},0,0,0,0,0,0,[Ext.dom,"Query",Ext.core,"DomQuery",Ext,"DomQuery"],function(){this._init()}));(Ext.cmd.derive("Ext.data.reader.Xml",Ext.data.reader.Reader,{alternateClassName:"Ext.data.XmlReader",config:{record:"",namespace:""},createAccessor:function(a){if(Ext.isEmpty(a)){return Ext.emptyFn}if(Ext.isFunction(a)){return a}return function(b){return this.getNodeValue(Ext.DomQuery.selectNode(a,b))}},getNodeValue:function(a){if(a){if(typeof a.normalize==="function"){a.normalize()}a=a.firstChild;if(a){return a.nodeValue}}return undefined},getResponseData:function(a){var c=a.responseXML,b="XML data not found in the response";if(!c){Ext.Logger.warn(b);return this.createReadError(b)}return c},getData:function(a){return a.documentElement||a},getRoot:function(b){var c=b.nodeName,a=this.getRootProperty();if(!a||(c&&c==a)){return b}else{if(Ext.DomQuery.isXml(b)){return Ext.DomQuery.selectNode(a,b)}}},extractData:function(a,b){var c=this.getRecord();if(c!==a.nodeName){a=Ext.DomQuery.select(c,a)}else{a=[a]}return Ext.data.reader.Reader.prototype.extractData.call(this,a,b)},readRecords:function(c,b,a){if(Ext.isArray(c)){c=c[0]}return Ext.data.reader.Reader.prototype.readRecords.call(this,c,b,a)},createFieldAccessor:function(e){var d=this,c=d.getNamespace(),b,a;b=e.mapping||((c?c+"|":"")+e.name);if(typeof b==="function"){a=function(g){return e.mapping(g,d)}}else{a=function(g){return d.getNodeValue(Ext.DomQuery.selectNode(b,g))}}return a},deprecated:{"5.1.1":{properties:{xmlData:null}}}},0,0,0,0,["reader.xml"],0,[Ext.data.reader,"Xml",Ext.data,"XmlReader"],0));(Ext.cmd.derive("Ext.data.writer.Xml",Ext.data.writer.Writer,{alternateClassName:"Ext.data.XmlWriter",config:{documentRoot:"xmlData",defaultDocumentRoot:"xmlData",header:"",record:"record"},selectorRe:/[^>\s]+/g,writeRecords:function(b,c){var j=this,g=[],d=0,h=c.length,l=j.getDocumentRoot(),k=j.getRecord(),e=k.match(this.selectorRe),m=e.length,n=c.length!==1&&m===1,a;a=this.getTransform();if(a){c=a(c,b)}g.push(j.getHeader()||"");if(!l&&n){l=j.getDefaultDocumentRoot()}if(l){g.push("<",l,">")}for(d=0;d")}k=e[d];for(d=0;d-1;d--){g.push("")}if(l){g.push("")}b.setXmlData(g.join(""));return b},objectToElement:function(b,e,g){var p,m,h=[],d,c,k,j,l,a,n;if(!g){g=[]}g.push("<",b);for(p in e){m=e[p];if(p[0]==="@"){g.push(" ",p.substr(1),'="',m,'"')}else{if(typeof m==="object"){this.objectToElement(p,m,h)}else{d=p.match(this.selectorRe);if((c=d.length)>1){l=l||{};for(j=l,k=0;k",m,"")}}}}g.push(">");g.push.apply(g,h);if(l){for(p in l){m=l[p];this.objectToElement(p,m,g)}}g.push("");return g}},0,0,0,0,["writer.xml"],0,[Ext.data.writer,"Xml",Ext.data,"XmlWriter"],0));(Ext.cmd.derive("Ext.data.XmlStore",Ext.data.Store,{constructor:function(a){a=Ext.apply({proxy:{type:"ajax",reader:"xml",writer:"xml"}},a);Ext.data.Store.prototype.constructor.call(this,a)}},1,0,0,0,["store.xml"],0,[Ext.data,"XmlStore"],0));(Ext.cmd.derive("Ext.data.identifier.Negative",Ext.data.identifier.Sequential,{config:{increment:-1,seed:-1}},0,0,0,0,["data.identifier.negative"],0,[Ext.data.identifier,"Negative"],0));(Ext.cmd.derive("Ext.data.identifier.Uuid",Ext.data.identifier.Generator,{isUnique:true,config:{id:null},constructor:function(a){Ext.data.identifier.Generator.prototype.constructor.call(this,a);this.reconfigure(a)},reconfigure:function(b){var a=this.self;this.generate=(b&&b.version===1)?a.createSequential(b.salt,b.timestamp,b.clockSeq):a.createRandom()},clone:null,statics:{createRandom:function(){var c="xxxxxxxx-xxxx-4xxx-Rxxx-xMxxxxxxxxxx".split(""),b="0123456789abcdef".split(""),a=c.length,d=[];return function(){for(var g,h,e=0;em){l=l.substring(l.length-m)}else{if(l.length>>8)&63),2)+a(e&255,2);g[4]=a(c,4)+a(i,8);return function(){g[0]=a(j,8);g[1]=a(d&65535,4);g[2]=a(((d>>>16)&4095)|(1<<12),4);++j;if(j>=k){j=0;++d}return g.join("-")}}}},1,0,0,0,["data.identifier.uuid"],0,[Ext.data.identifier,"Uuid"],function(){this.Global=new this({id:"uuid"})}));(Ext.cmd.derive("Ext.data.proxy.WebStorage",Ext.data.proxy.Client,{alternateClassName:"Ext.data.WebStorageProxy",config:{id:undefined},constructor:function(a){Ext.data.proxy.Client.prototype.constructor.apply(this,arguments);this.cache={};this.initialize()},create:function(e){var k=this,d=e.getRecords(),c=d.length,a=k.getIds(),b,h,g,j;if(k.isHierarchical===undefined){k.isHierarchical=!!d[0].isNode;if(k.isHierarchical){k.getStorageObject().setItem(k.getTreeKey(),true)}}for(g=0;gb){h=e._bothMsg}}else{if(c){if(gb){h=e._maxMsg}}}}return h},validateValue:function(a){if(a===undefined||a===null){return this.getEmptyMessage()}return true},getValue:Ext.identityFn},1,0,0,0,["data.validator.bound"],0,[Ext.data.validator,"Bound"],0));(Ext.cmd.derive("Ext.data.validator.Format",Ext.data.validator.Validator,{type:"format",config:{message:"Is in the wrong format",matcher:undefined},validate:function(b){var c=this.getMatcher(),a=c&&c.test(b);return a?a:this.getMessage()}},0,0,0,0,["data.validator.format"],0,[Ext.data.validator,"Format"],0));(Ext.cmd.derive("Ext.data.validator.Email",Ext.data.validator.Format,{type:"email",config:{message:"Is not a valid email address",matcher:/^(")?(?:[^\."])(?:(?:[\.])?(?:[\w\-!#$%&'*+\/=?\^_`{|}~]))*\1@(\w[\-\w]*\.){1,5}([A-Za-z]){2,6}$/}},0,0,0,0,["data.validator.email"],0,[Ext.data.validator,"Email"],0));(Ext.cmd.derive("Ext.data.validator.List",Ext.data.validator.Validator,{type:"list",config:{list:null},inclusion:null,validate:function(c){var b=Ext.Array.contains(this.getList(),c),e=this.inclusion,d=!e,a;a=(e&&b)||(d&&!b);return a||this.getMessage()}},0,0,0,0,["data.validator.list"],0,[Ext.data.validator,"List"],0));(Ext.cmd.derive("Ext.data.validator.Exclusion",Ext.data.validator.List,{type:"exclusion",config:{message:"Is a value that has been excluded"},inclusion:false},0,0,0,0,["data.validator.exclusion"],0,[Ext.data.validator,"Exclusion"],0));(Ext.cmd.derive("Ext.data.validator.Inclusion",Ext.data.validator.List,{type:"inclusion",config:{message:"Is not in the list of acceptable values"},inclusion:true},0,0,0,0,["data.validator.inclusion"],0,[Ext.data.validator,"Inclusion"],0));(Ext.cmd.derive("Ext.data.validator.Length",Ext.data.validator.Bound,{type:"length",config:{minOnlyMessage:"Length must be at least {0}",maxOnlyMessage:"Length must be no more than {0}",bothMessage:"Length must be between {0} and {1}"},getValue:function(a){return String(a).length}},0,0,0,0,["data.validator.length"],0,[Ext.data.validator,"Length"],0));(Ext.cmd.derive("Ext.data.validator.Presence",Ext.data.validator.Validator,{type:"presence",config:{message:"Must be present",allowEmpty:false},validate:function(b){var a=!(b===undefined||b===null);if(a&&!this.getAllowEmpty()){a=!(b==="")}return a?true:this.getMessage()}},0,0,0,0,["data.validator.presence"],0,[Ext.data.validator,"Presence"],0));(Ext.cmd.derive("Ext.data.validator.Range",Ext.data.validator.Bound,{type:"range",config:{minOnlyMessage:"Must be must be at least {0}",maxOnlyMessage:"Must be no more than than {0}",bothMessage:"Must be between {0} and {1}",nanMessage:"Must be numeric"},validateValue:function(a){var b=Ext.data.validator.Bound.prototype.validateValue.call(this,a);if(b===true&&isNaN(a)){b=this.getNanMessage()}return b}},0,0,0,0,["data.validator.range"],0,[Ext.data.validator,"Range"],0));(Ext.cmd.derive("Ext.direct.Event",Ext.Base,{status:true,constructor:function(a){Ext.apply(this,a)},getName:function(){return this.name},getData:function(){return this.data}},1,0,0,0,["direct.event"],0,[Ext.direct,"Event"],0));(Ext.cmd.derive("Ext.direct.RemotingEvent",Ext.direct.Event,{getTransaction:function(){var a=this;return a.transaction||Ext.direct.Manager.getTransaction(a.tid)}},0,0,0,0,["direct.rpc"],0,[Ext.direct,"RemotingEvent"],0));(Ext.cmd.derive("Ext.direct.ExceptionEvent",Ext.direct.RemotingEvent,{status:false},0,0,0,0,["direct.exception"],0,[Ext.direct,"ExceptionEvent"],0));(Ext.cmd.derive("Ext.direct.JsonProvider",Ext.direct.Provider,{parseResponse:function(a){var b=a&&a.responseText;if(b){if(Ext.isObject(b)||Ext.isArray(b)){return b}return Ext.decode(b)}return null},createEvents:function(b){var h=this,j=null,d=[],g,c,a;try{j=h.parseResponse(b)}catch(k){g=new Ext.direct.ExceptionEvent({data:k,xhr:b,code:Ext.direct.Manager.exceptions.PARSE,message:"Error parsing json response: \n\n "+k});return[g]}if(Ext.isArray(j)){for(c=0,a=j.length;c0){if(j){for(d=0,e=j.length;d0){c.sendRequest(a===1?b[0]:b);c.callBuffer=[]}},configureTransaction:function(d,a,g,h){var e,b,i,j,c;e=a.getCallData(g);b=e.callback;i=e.scope;j=e.options;b=b&&i?Ext.Function.bind(b,i):b;c=Ext.apply({},{provider:this,args:g,action:d,method:a.name,form:e.form,data:e.data,metadata:e.metadata,callbackOptions:j,callback:b,isForm:h,disableBatching:a.disableBatching});if(j&&j.timeout!=null){c.timeout=j.timeout}return new Ext.direct.Transaction(c)},configureRequest:function(c,e,a){var b=this,d;d=b.configureTransaction(c,e,a);if(b.fireEvent("beforecall",b,d,e)!==false){Ext.direct.Manager.addTransaction(d);b.queueTransaction(d);b.fireEvent("call",b,d,e)}},configureFormRequest:function(e,i,b){var d=this,g,c,a,h;g=d.configureTransaction(e,i,b,true);if(d.fireEvent("beforecall",d,g,i)!==false){Ext.direct.Manager.addTransaction(g);c=g.form;a=String(c.getAttribute("enctype")).toLowerCase()==="multipart/form-data";h={extTID:g.id,extAction:e,extMethod:i.name,extType:"rpc",extUpload:String(a)};if(g.metadata){h.extMetadata=Ext.JSON.encode(g.metadata)}Ext.apply(g,{form:c,isUpload:a,params:h});d.sendFormRequest(g);d.fireEvent("call",d,g,i)}},sendFormRequest:function(b){var a=this;Ext.Ajax.request({url:a.url,params:b.params,callback:a.onData,scope:a,form:b.form,isUpload:b.isUpload,transaction:b})},inheritableStatics:{checkConfig:function(a){return a&&a.type==="remoting"&&a.url&&Ext.isArray(a.actions)}}},1,0,0,0,["direct.remotingprovider"],0,[Ext.direct,"RemotingProvider"],0));(Ext.cmd.derive("Ext.dom.GarbageCollector",Ext.Base,{singleton:true,interval:30000,constructor:function(){var a=this;a.collect=Ext.Function.bind(a.collect,a);a.lastTime=Ext.now();a.resume()},collect:function(){var j=this,a=Ext.cache,b,g,c,k,h,d;for(b in a){if(!a.hasOwnProperty(b)){continue}c=a[b];if(c.skipGarbageCollection){continue}g=c.dom;try{h=Ext.isGarbage(g)}catch(i){delete a[b];continue}if(h){if(c&&c.dom){c.collect()}}}if(Ext.isIE9m){k={};for(b in a){if(a.hasOwnProperty(b)){k[b]=a[b]}}Ext.cache=Ext.dom.Element.cache=k}j.lastTime=Ext.now()},pause:function(){clearTimeout(this.timerId)},resume:function(){var a=this,b=a.lastTime;if(Ext.enableGarbageCollector&&(Ext.now()-b>a.interval)){a.collect()}a.timerId=Ext.interval(a.collect,a.interval)}},1,0,0,0,0,0,[Ext.dom,"GarbageCollector"],0));(Ext.cmd.derive("Ext.event.gesture.Recognizer",Ext.Base,{priority:0,handledEvents:[],config:{onRecognized:Ext.emptyFn,callbackScope:null},constructor:function(a){this.initConfig(a);Ext.event.publisher.Gesture.instance.registerRecognizer(this)},onStart:Ext.emptyFn,onEnd:Ext.emptyFn,onTouchStart:Ext.emptyFn,onTouchMove:Ext.emptyFn,onTouchEnd:Ext.emptyFn,onTouchCancel:Ext.emptyFn,fail:function(){return false},fire:function(){this.getOnRecognized().apply(this.getCallbackScope(),arguments)},reset:Ext.emptyFn},1,0,0,0,0,[[Ext.mixin.Identifiable.prototype.mixinId||Ext.mixin.Identifiable.$className,Ext.mixin.Identifiable]],[Ext.event.gesture,"Recognizer"],0));(Ext.cmd.derive("Ext.event.gesture.SingleTouch",Ext.event.gesture.Recognizer,{inheritableStatics:{NOT_SINGLE_TOUCH:"Not Single Touch",TOUCH_MOVED:"Touch Moved",EVENT_CANCELED:"Event Canceled"},onTouchStart:function(a){if(a.touches.length>1){return this.fail(this.self.NOT_SINGLE_TOUCH)}},onTouchCancel:function(){return false}},0,0,0,0,0,0,[Ext.event.gesture,"SingleTouch"],0));(Ext.cmd.derive("Ext.event.gesture.DoubleTap",Ext.event.gesture.SingleTouch,{priority:300,inheritableStatics:{DIFFERENT_TARGET:"Different Target"},config:{moveDistance:8,tapDistance:24,maxDuration:300},handledEvents:["singletap","doubletap"],singleTapTimer:null,startTime:0,lastTapTime:0,onTouchStart:function(c){var b=this,a;if(Ext.event.gesture.SingleTouch.prototype.onTouchStart.apply(this,arguments)===false){return false}a=b.lastStartPoint=c.changedTouches[0].point;b.startPoint=b.startPoint||a;b.startTime=c.time;clearTimeout(b.singleTapTimer)},onTouchMove:function(c){var b=this,a=c.changedTouches[0].point;if(Math.abs(a.getDistanceTo(b.lastStartPoint))>=b.getMoveDistance()){b.startPoint=null;return b.fail(b.self.TOUCH_MOVED)}},onTouchEnd:function(g){var i=this,d=i.getMaxDuration(),a=g.time,h=g.target,j=i.lastTapTime,b=i.lastTarget,k=g.changedTouches[0].point,c;i.lastTapTime=a;i.lastTarget=h;if(j){c=a-j;if(c<=d&&Math.abs(k.getDistanceTo(i.startPoint))<=i.getTapDistance()){if(h!==b){return i.fail(i.self.DIFFERENT_TARGET)}i.lastTarget=null;i.lastTapTime=0;i.fire("doubletap",g,{touch:g.changedTouches[0],duration:c});i.startPoint=null;return}}if(a-i.startTime>d){i.fireSingleTap(g)}else{i.setSingleTapTimer(g)}},setSingleTapTimer:function(b){var a=this;a.singleTapTimer=Ext.defer(function(){a.fireSingleTap(b)},a.getMaxDuration())},fireSingleTap:function(a,b){this.fire("singletap",a,{touch:b});this.startPoint=null},reset:function(){var a=this;a.startTime=a.lastTapTime=0;a.lastStartPoint=a.startPoint=a.singleTapTimer=null}},0,0,0,0,0,0,[Ext.event.gesture,"DoubleTap"],function(a){var b=Ext.manifest.gestures;a.instance=new a(b&&b.doubleTap)}));(Ext.cmd.derive("Ext.event.gesture.Drag",Ext.event.gesture.SingleTouch,{priority:100,isStarted:false,startPoint:null,previousPoint:null,lastPoint:null,handledEvents:["dragstart","drag","dragend","dragcancel"],config:{minDistance:8},constructor:function(){Ext.event.gesture.SingleTouch.prototype.constructor.apply(this,arguments);this.initInfo()},initInfo:function(){this.info={touch:null,previous:{x:0,y:0},x:0,y:0,delta:{x:0,y:0},absDelta:{x:0,y:0},flick:{velocity:{x:0,y:0}},direction:{x:0,y:0},time:0,previousTime:{x:0,y:0}}},onTouchStart:function(a){if(Ext.event.gesture.SingleTouch.prototype.onTouchStart.apply(this,arguments)===false){if(this.isStarted&&this.lastMoveEvent!==null){this.lastMoveEvent.isStopped=false;this.onTouchEnd(this.lastMoveEvent)}return false}this.startTime=a.time;this.startPoint=a.changedTouches[0].point},tryDragStart:function(d){var b=this.startPoint,h=d.changedTouches[0],a=h.point,g=this.getMinDistance(),c=this.info;if(Math.abs(a.getDistanceTo(b))>=g){this.isStarted=true;this.previousPoint=this.lastPoint=a;this.resetInfo("x",d,h);this.resetInfo("y",d,h);c.time=d.time;this.fire("dragstart",d,c)}},onTouchMove:function(b){if(!this.isStarted){this.tryDragStart(b)}if(!this.isStarted){return}var c=b.changedTouches[0],a=c.point;if(this.lastPoint){this.previousPoint=this.lastPoint}this.lastPoint=a;this.lastMoveEvent=b;this.updateInfo("x",b,c);this.updateInfo("y",b,c);this.info.time=b.time;this.fire("drag",b,this.info)},onAxisDragEnd:function(a,c){var b=c.time-c.previousTime[a];if(b>0){c.flick.velocity[a]=(c[a]-c.previous[a])/b}},resetInfo:function(c,h,j){var d=this.lastPoint[c],b=this.startPoint[c],i=d-b,a=c.toUpperCase(),g=this.info;g.touch=j;g.delta[c]=i;g.absDelta[c]=Math.abs(i);g.previousTime[c]=this.startTime;g.previous[c]=b;g[c]=d;g.direction[c]=0;g["start"+a]=this.startPoint[c];g["previous"+a]=g.previous[c];g["page"+a]=g[c];g["delta"+a]=g.delta[c];g["absDelta"+a]=g.absDelta[c];g["previousDelta"+a]=0;g.startTime=this.startTime},updateInfo:function(d,j,i){var k=this,m=k.lastPoint[d],g=k.previousPoint[d],a=k.startPoint[d],n=m-a,c=k.info,l=c.direction,h=d.toUpperCase(),b=c.previous[d];c.touch=i;c.delta[d]=n;c.absDelta[d]=Math.abs(n);if(m!==b&&m!==c[d]){c.previous[d]=c[d];c.previousTime[d]=c.time}c[d]=m;if(m>g){l[d]=1}else{if(mthis.getMaxDuration()){return this.fail(this.self.MAX_DURATION_EXCEEDED)}if(this.isHorizontal&&c>this.getMaxOffset()){this.isHorizontal=false}if(this.isVertical&&d>this.getMaxOffset()){this.isVertical=false}if(!this.isVertical||!this.isHorizontal){if(this.isHorizontal&&dg){this.isVertical=false}if(this.isHorizontal&&b>g){this.isHorizontal=false}if(this.isVertical&&this.isHorizontal){if(b>c){this.isHorizontal=false}else{this.isVertical=false}}if(this.isHorizontal){n=(i<0)?"left":"right";a=i}else{if(this.isVertical){n=(h<0)?"up":"down";a=h}}n=this.direction||(this.direction=n);if(n==="up"){a=h*-1}else{if(n==="left"){a=i*-1}}this.distance=a;if(!a){return this.fail(this.self.DISTANCE_NOT_ENOUGH)}if(!this.started){if(n==="right"&&this.startX>q){return this.fail(this.self.NOT_NEAR_EDGE)}else{if(n==="down"&&this.startY>q){return this.fail(this.self.NOT_NEAR_EDGE)}else{if(n==="left"&&(o-this.startX)>q){return this.fail(this.self.NOT_NEAR_EDGE)}else{if(n==="up"&&(k-this.startY)>q){return this.fail(this.self.NOT_NEAR_EDGE)}}}}this.started=true;this.startTime=l.time;this.fire("edgeswipestart",l,{touch:j,direction:n,distance:a,duration:d})}else{this.fire("edgeswipe",l,{touch:j,direction:n,distance:a,duration:d})}},onTouchEnd:function(b){var a;if(this.onTouchMove(b)!==false){a=b.time-this.startTime;this.fire("edgeswipeend",b,{touch:b.changedTouches[0],direction:this.direction,distance:this.distance,duration:a})}},onTouchCancel:function(a){this.fire("edgeswipecancel",a,{touch:a.changedTouches[0]});return false},reset:function(){var a=this;a.started=a.direction=a.isHorizontal=a.isVertical=a.startX=a.startY=a.startTime=a.distance=null}},0,0,0,0,0,0,[Ext.event.gesture,"EdgeSwipe"],function(a){var b=Ext.manifest.gestures;a.instance=new a(b&&b.edgeSwipe)}));(Ext.cmd.derive("Ext.event.gesture.LongPress",Ext.event.gesture.SingleTouch,{priority:400,inheritableStatics:{DURATION_NOT_ENOUGH:"Duration Not Enough"},config:{moveDistance:8,minDuration:1000},handledEvents:["longpress","taphold"],fireLongPress:function(a){this.fire("longpress",a,{touch:a.changedTouches[0],duration:this.getMinDuration()});this.isLongPress=true},onTouchStart:function(a){if(Ext.event.gesture.SingleTouch.prototype.onTouchStart.apply(this,arguments)===false){return false}this.startPoint=a.changedTouches[0].point;this.isLongPress=false;this.setLongPressTimer(a)},setLongPressTimer:function(b){var a=this;a.timer=Ext.defer(function(){a.fireLongPress(b)},a.getMinDuration())},onTouchMove:function(b){var a=b.changedTouches[0].point;if(Math.abs(a.getDistanceTo(this.startPoint))>=this.getMoveDistance()){return this.fail(this.self.TOUCH_MOVED)}},onTouchEnd:function(){if(!this.isLongPress){return this.fail(this.self.DURATION_NOT_ENOUGH)}},fail:function(){clearTimeout(this.timer);return Ext.event.gesture.SingleTouch.prototype.fail.apply(this,arguments)},reset:function(){this.isLongPress=this.startPoint=null},fire:function(a){if(a==="longpress"){var b=Array.prototype.slice.call(arguments);b[0]="taphold";this.fire.apply(this,b)}return Ext.event.gesture.SingleTouch.prototype.fire.apply(this,arguments)}},0,0,0,0,0,0,[Ext.event.gesture,"LongPress"],function(a){var b=Ext.manifest.gestures;a.instance=new a(b&&b.longPress)}));(Ext.cmd.derive("Ext.event.gesture.MultiTouch",Ext.event.gesture.Recognizer,{requiredTouchesCount:2,isTracking:false,isStarted:false,onTouchStart:function(d){var a=this.requiredTouchesCount,c=d.touches,b=c.length;if(b===a){this.start(d)}else{if(b>a){this.end(d)}}},onTouchEnd:function(a){this.end(a)},onTouchCancel:function(a){this.end(a,true);return false},start:function(){if(!this.isTracking){this.isTracking=true;this.isStarted=false}},end:function(b,a){if(this.isTracking){this.isTracking=false;if(this.isStarted){this.isStarted=false;this[a?"fireCancel":"fireEnd"](b)}}},reset:function(){this.isTracking=this.isStarted=false}},0,0,0,0,0,0,[Ext.event.gesture,"MultiTouch"],0));(Ext.cmd.derive("Ext.event.gesture.Pinch",Ext.event.gesture.MultiTouch,{priority:600,handledEvents:["pinchstart","pinch","pinchend","pinchcancel"],startDistance:0,lastTouches:null,onTouchMove:function(c){if(!this.isTracking){return}var b=c.touches,d,a,g;d=b[0].point;a=b[1].point;g=d.getDistanceTo(a);if(g===0){return}if(!this.isStarted){this.isStarted=true;this.startDistance=g;this.fire("pinchstart",c,{touches:b,distance:g,scale:1})}else{this.fire("pinch",c,{touches:b,distance:g,scale:g/this.startDistance})}},fireEnd:function(a){this.fire("pinchend",a)},fireCancel:function(a){this.fire("pinchcancel",a)},fail:function(){return Ext.event.gesture.MultiTouch.prototype.fail.apply(this,arguments)},reset:function(){this.lastTouches=null;this.startDistance=0;Ext.event.gesture.MultiTouch.prototype.reset.call(this)}},0,0,0,0,0,0,[Ext.event.gesture,"Pinch"],function(a){var b=Ext.manifest.gestures;a.instance=new a(b&&b.pinch)}));(Ext.cmd.derive("Ext.event.gesture.Rotate",Ext.event.gesture.MultiTouch,{priority:700,handledEvents:["rotatestart","rotate","rotateend","rotatecancel"],startAngle:0,lastTouches:null,lastAngle:null,onTouchMove:function(i){if(!this.isTracking){return}var h=i.touches,b=this.lastAngle,d,g,c,a,j,k;d=h[0].point;g=h[1].point;c=d.getAngleTo(g);if(b!==null){k=Math.abs(b-c);a=c+360;j=c-360;if(Math.abs(a-b)=this.getMoveDistance()){this.fire("tapcancel",b,{touch:c});return this.fail(this.self.TOUCH_MOVED)}},onTouchEnd:function(a){this.fire("tap",a,{touch:a.changedTouches[0]})},onTouchCancel:function(a){this.fire("tapcancel",a,{touch:a.changedTouches[0]});return false},reset:function(){this.startPoint=null}},0,0,0,0,0,0,[Ext.event.gesture,"Tap"],function(b){var a=Ext.manifest.gestures;b.instance=new b(a&&a.tap)}));(Ext.cmd.derive("Ext.event.publisher.Focus",Ext.event.publisher.Dom,{type:"focus",handledEvents:["focusenter","focusleave","focusmove"],handledDomEvents:["focusin","focusout"],doDelegatedEvent:function(d,c){var b=this,a;d=Ext.event.publisher.Dom.prototype.doDelegatedEvent.call(this,d,false);if(d){if(d.type==="focusout"){if(d.relatedTarget==null){b.processFocusIn(d,d.target,document.body,c)}}else{a=d.relatedTarget;b.processFocusIn(d,(a==null||!a.tagName)?document.body:a,d.target,c)}}},processFocusIn:function(j,k,c,m){var l=this,n,b,i=[],a,h,g,d;g=Ext.fly(k);d=Ext.fly(c);if((g&&g.isFocusSuspended())||(d&&d.isFocusSuspended())){return}for(b=k,n=Ext.dom.Element.getCommonAncestor(c,k,true);b&&b!==n;b=b.parentNode){i.push(b)}if(i.length){a=l.createSyntheticEvent("focusleave",j,k,c);l.publish("focusleave",i,a);if(a.isStopped){return}}i.length=0;for(b=c;b!==n;b=b.parentNode){i.push(b)}h=l.createSyntheticEvent("focusenter",j,c,k);if(i.length){l.publish("focusenter",i,h);if(h.isStopped){return}}i=l.getPropagatingTargets(n);if(i.length){a=l.createSyntheticEvent("focusmove",j,c,k);l.publish("focusmove",i,a);if(a.isStopped){return}}if(m){l.afterEvent(j)}Ext.GlobalEvents.fireEvent("focus",{event:h,toElement:c,fromElement:k})},createSyntheticEvent:function(b,e,d,a){var c=new Ext.event.Event(e);c.type=b;c.relatedTarget=a;c.target=d;return c}},0,0,0,0,0,0,[Ext.event.publisher,"Focus"],function(b){var a;b.instance=new b();if(!Ext.supports.FocusinFocusoutEvents){this.override({handledDomEvents:["focus","blur"],doDelegatedEvent:function(h,g){var d=this;h=d.callSuper([h,false]);if(h){clearTimeout(a);a=0;if(h.type==="blur"){var c=h.target===window?document.body:h.target;a=setTimeout(function(){a=0;d.processFocusIn(h,c,document.body,g);b.previousActiveElement=null},0);if(h.target===window||h.target===document){b.previousActiveElement=null}else{b.previousActiveElement=h.target}}else{d.processFocusIn(h,b.previousActiveElement||document.body,h.target===window?document.body:h.target,g)}}}})}}));(Ext.cmd.derive("Ext.fx.State",Ext.Base,{isAnimatable:{"background-color":true,"background-image":true,"background-position":true,"border-bottom-color":true,"border-bottom-width":true,"border-color":true,"border-left-color":true,"border-left-width":true,"border-right-color":true,"border-right-width":true,"border-spacing":true,"border-top-color":true,"border-top-width":true,"border-width":true,bottom:true,color:true,crop:true,"font-size":true,"font-weight":true,height:true,left:true,"letter-spacing":true,"line-height":true,"margin-bottom":true,"margin-left":true,"margin-right":true,"margin-top":true,"max-height":true,"max-width":true,"min-height":true,"min-width":true,opacity:true,"outline-color":true,"outline-offset":true,"outline-width":true,"padding-bottom":true,"padding-left":true,"padding-right":true,"padding-top":true,right:true,"text-indent":true,"text-shadow":true,top:true,"vertical-align":true,visibility:true,width:true,"word-spacing":true,"z-index":true,zoom:true,transform:true},constructor:function(a){this.data={};this.set(a)},setConfig:function(a){this.set(a);return this},setRaw:function(a){this.data=a;return this},clear:function(){return this.setRaw({})},setTransform:function(c,h){var g=this.data,a=Ext.isArray(h),b=g.transform,e,d;if(!b){b=g.transform={translateX:0,translateY:0,translateZ:0,scaleX:1,scaleY:1,scaleZ:1,rotate:0,rotateX:0,rotateY:0,rotateZ:0,skewX:0,skewY:0}}if(typeof c=="string"){switch(c){case"translate":if(a){e=h.length;if(e==0){break}b.translateX=h[0];if(e==1){break}b.translateY=h[1];if(e==2){break}b.translateZ=h[2]}else{b.translateX=h}break;case"rotate":if(a){e=h.length;if(e==0){break}b.rotateX=h[0];if(e==1){break}b.rotateY=h[1];if(e==2){break}b.rotateZ=h[2]}else{b.rotate=h}break;case"scale":if(a){e=h.length;if(e==0){break}b.scaleX=h[0];if(e==1){break}b.scaleY=h[1];if(e==2){break}b.scaleZ=h[2]}else{b.scaleX=h;b.scaleY=h}break;case"skew":if(a){e=h.length;if(e==0){break}b.skewX=h[0];if(e==1){break}b.skewY=h[1]}else{b.skewX=h}break;default:b[c]=h}}else{for(d in c){if(c.hasOwnProperty(d)){h=c[d];this.setTransform(d,h)}}}},set:function(a,d){var c=this.data,b;if(typeof a!="string"){for(b in a){d=a[b];if(b==="transform"){this.setTransform(d)}else{c[b]=d}}}else{if(a==="transform"){this.setTransform(d)}else{c[a]=d}}return this},unset:function(a){var b=this.data;if(b.hasOwnProperty(a)){delete b[a]}return this},getData:function(){return this.data}},1,0,0,0,0,0,[Ext.fx,"State"],0));(Ext.cmd.derive("Ext.fx.animation.Abstract",Ext.Evented,{isAnimation:true,config:{name:"",element:null,before:null,from:{},to:{},after:null,states:{},duration:300,easing:"linear",iteration:1,direction:"normal",delay:0,onBeforeStart:null,callback:null,onEnd:null,onBeforeEnd:null,scope:null,reverse:null,preserveEndState:false,replacePrevious:true},STATE_FROM:"0%",STATE_TO:"100%",DIRECTION_UP:"up",DIRECTION_DOWN:"down",DIRECTION_LEFT:"left",DIRECTION_RIGHT:"right",stateNameRegex:/^(?:[\d\.]+)%$/,constructor:function(){this.states={};Ext.Evented.prototype.constructor.apply(this,arguments);return this},applyElement:function(a){return Ext.get(a)},applyBefore:function(a,b){if(a){return Ext.factory(a,Ext.fx.State,b)}},applyAfter:function(b,a){if(b){return Ext.factory(b,Ext.fx.State,a)}},setFrom:function(a){return this.setState(this.STATE_FROM,a)},setTo:function(a){return this.setState(this.STATE_TO,a)},getFrom:function(){return this.getState(this.STATE_FROM)},getTo:function(){return this.getState(this.STATE_TO)},setStates:function(a){var c=this.stateNameRegex,b;for(b in a){if(c.test(b)){this.setState(b,a[b])}}return this},getStates:function(){return this.states},updateCallback:function(a){if(a){this.setOnEnd(a)}},end:function(){this.stop()},stop:function(){this.fireEvent("stop",this)},destroy:function(){this.stop();Ext.Evented.prototype.destroy.call(this)},setState:function(b,d){var a=this.getStates(),c;c=Ext.factory(d,Ext.fx.State,a[b]);if(c){a[b]=c}return this},getState:function(a){return this.getStates()[a]},getData:function(){var h=this,m=h.getStates(),e={},i=h.getBefore(),c=h.getAfter(),j=m[h.STATE_FROM],k=m[h.STATE_TO],l=j.getData(),g=k.getData(),d,b,a;for(b in m){if(m.hasOwnProperty(b)){a=m[b];d=a.getData();e[b]=d}}return{before:i?i.getData():{},after:c?c.getData():{},states:e,from:l,to:g,duration:h.getDuration(),iteration:h.getIteration(),direction:h.getDirection(),easing:h.getEasing(),delay:h.getDelay(),onEnd:h.getOnEnd(),onBeforeEnd:h.getOnBeforeEnd(),onBeforeStart:h.getOnBeforeStart(),scope:h.getScope(),preserveEndState:h.getPreserveEndState(),replacePrevious:h.getReplacePrevious()}}},1,0,0,0,0,0,[Ext.fx.animation,"Abstract"],0));(Ext.cmd.derive("Ext.fx.animation.Slide",Ext.fx.animation.Abstract,{alternateClassName:"Ext.fx.animation.SlideIn",config:{direction:"left",out:false,offset:0,easing:"auto",containerBox:"auto",elementBox:"auto",isElementBoxFit:true,useCssTransform:true},reverseDirectionMap:{up:"down",down:"up",left:"right",right:"left"},applyEasing:function(a){if(a==="auto"){return"ease-"+((this.getOut())?"in":"out")}return a},getContainerBox:function(){var a=this._containerBox;if(a==="auto"){a=this.getElement().getParent().getBox()}return a},getElementBox:function(){var a=this._elementBox;if(this.getIsElementBoxFit()){return this.getContainerBox()}if(a==="auto"){a=this.getElement().getBox()}return a},getData:function(){var q=this.getElementBox(),c=this.getContainerBox(),h=q?q:c,o=this.getFrom(),p=this.getTo(),g=this.getOut(),e=this.getOffset(),n=this.getDirection(),b=this.getUseCssTransform(),i=this.getReverse(),d=0,a=0,m,k,l,j;if(i){n=this.reverseDirectionMap[n]}switch(n){case this.DIRECTION_UP:if(g){a=c.top-h.top-h.height-e}else{a=c.bottom-h.bottom+h.height+e}break;case this.DIRECTION_DOWN:if(g){a=c.bottom-h.bottom+h.height+e}else{a=c.top-h.height-h.top-e}break;case this.DIRECTION_RIGHT:if(g){d=c.right-h.right+h.width+e}else{d=c.left-h.left-h.width-e}break;case this.DIRECTION_LEFT:if(g){d=c.left-h.left-h.width-e}else{d=c.right-h.right+h.width+e}break}m=(g)?0:d;k=(g)?0:a;if(b){o.setTransform({translateX:m,translateY:k})}else{o.set("left",m);o.set("top",k)}l=(g)?d:0;j=(g)?a:0;if(b){p.setTransform({translateX:l,translateY:j})}else{p.set("left",l);p.set("top",j)}return Ext.fx.animation.Abstract.prototype.getData.apply(this,arguments)}},0,0,0,0,["animation.slide","animation.slideIn"],0,[Ext.fx.animation,"Slide",Ext.fx.animation,"SlideIn"],0));(Ext.cmd.derive("Ext.fx.animation.SlideOut",Ext.fx.animation.Slide,{config:{out:true}},0,0,0,0,["animation.slideOut"],0,[Ext.fx.animation,"SlideOut"],0));(Ext.cmd.derive("Ext.fx.animation.Fade",Ext.fx.animation.Abstract,{alternateClassName:"Ext.fx.animation.FadeIn",config:{out:false,before:{display:null,opacity:0},after:{opacity:null},reverse:null},updateOut:function(a){var c=this.getTo(),b=this.getFrom();if(a){b.set("opacity",1);c.set("opacity",0)}else{b.set("opacity",0);c.set("opacity",1)}}},0,0,0,0,["animation.fade","animation.fadeIn"],0,[Ext.fx.animation,"Fade",Ext.fx.animation,"FadeIn"],0));(Ext.cmd.derive("Ext.fx.animation.FadeOut",Ext.fx.animation.Fade,{config:{out:true,before:{}}},0,0,0,0,["animation.fadeOut"],0,[Ext.fx.animation,"FadeOut"],0));(Ext.cmd.derive("Ext.fx.animation.Flip",Ext.fx.animation.Abstract,{config:{easing:"ease-in",direction:"right",half:false,out:null},getData:function(){var h=this,j=h.getFrom(),k=h.getTo(),i=h.getDirection(),b=h.getOut(),n=h.getHalf(),c=n?90:180,e=1,a=1,m=0,l=0,g=0,d=0;if(b){a=0.8}else{e=0.8}switch(i){case this.DIRECTION_UP:if(b){g=c}else{m=-c}break;case this.DIRECTION_DOWN:if(b){g=-c}else{m=c}break;case this.DIRECTION_RIGHT:if(b){d=c}else{l=-c}break;case this.DIRECTION_LEFT:if(b){d=-c}else{l=c}break}j.setTransform({rotateX:m,rotateY:l,scale:e});k.setTransform({rotateX:g,rotateY:d,scale:a});return Ext.fx.animation.Abstract.prototype.getData.call(this)}},0,0,0,0,["animation.flip"],0,[Ext.fx.animation,"Flip"],0));(Ext.cmd.derive("Ext.fx.animation.Pop",Ext.fx.animation.Abstract,{alternateClassName:"Ext.fx.animation.PopIn",config:{out:false,before:{display:null,opacity:0},after:{opacity:null}},getData:function(){var c=this.getTo(),b=this.getFrom(),a=this.getOut();if(a){b.set("opacity",1);b.setTransform({scale:1});c.set("opacity",0);c.setTransform({scale:0})}else{b.set("opacity",0);b.setTransform({scale:0});c.set("opacity",1);c.setTransform({scale:1})}return Ext.fx.animation.Abstract.prototype.getData.apply(this,arguments)}},0,0,0,0,["animation.pop","animation.popIn"],0,[Ext.fx.animation,"Pop",Ext.fx.animation,"PopIn"],0));(Ext.cmd.derive("Ext.fx.animation.PopOut",Ext.fx.animation.Pop,{config:{out:true,before:{}}},0,0,0,0,["animation.popOut"],0,[Ext.fx.animation,"PopOut"],0));(Ext.cmd.derive("Ext.fx.Animation",Ext.Base,{constructor:function(b){var a=Ext.fx.animation.Abstract,c;if(typeof b=="string"){c=b;b={}}else{if(b&&b.type){c=b.type}}if(c){a=Ext.ClassManager.getByAlias("animation."+c)}return Ext.factory(b,a)}},1,0,0,0,0,0,[Ext.fx,"Animation"],0));(Ext.cmd.derive("Ext.fx.runner.Css",Ext.Evented,{prefixedProperties:{transform:true,"transform-origin":true,perspective:true,"transform-style":true,transition:true,"transition-property":true,"transition-duration":true,"transition-timing-function":true,"transition-delay":true,animation:true,"animation-name":true,"animation-duration":true,"animation-iteration-count":true,"animation-direction":true,"animation-timing-function":true,"animation-delay":true},lengthProperties:{top:true,right:true,bottom:true,left:true,width:true,height:true,"max-height":true,"max-width":true,"min-height":true,"min-width":true,"margin-bottom":true,"margin-left":true,"margin-right":true,"margin-top":true,"padding-bottom":true,"padding-left":true,"padding-right":true,"padding-top":true,"border-bottom-width":true,"border-left-width":true,"border-right-width":true,"border-spacing":true,"border-top-width":true,"border-width":true,"outline-width":true,"letter-spacing":true,"line-height":true,"text-indent":true,"word-spacing":true,"font-size":true,translate:true,translateX:true,translateY:true,translateZ:true,translate3d:true},durationProperties:{"transition-duration":true,"transition-delay":true,"animation-duration":true,"animation-delay":true},angleProperties:{rotate:true,rotateX:true,rotateY:true,rotateZ:true,skew:true,skewX:true,skewY:true},lengthUnitRegex:/([a-z%]*)$/,DEFAULT_UNIT_LENGTH:"px",DEFAULT_UNIT_ANGLE:"deg",DEFAULT_UNIT_DURATION:"ms",formattedNameCache:{},transformMethods3d:["translateX","translateY","translateZ","rotate","rotateX","rotateY","rotateZ","skewX","skewY","scaleX","scaleY","scaleZ"],transformMethodsNo3d:["translateX","translateY","rotate","skewX","skewY","scaleX","scaleY"],constructor:function(){var a=this;a.transformMethods=Ext.feature.has.Css3dTransforms?a.transformMethods3d:a.transformMethodsNo3d;a.vendorPrefix=Ext.browser.getStyleDashPrefix();a.ruleStylesCache={};Ext.Evented.prototype.constructor.call(this)},getStyleSheet:function(){var c=this.styleSheet,a,b;if(!c){a=document.createElement("style");a.type="text/css";(document.head||document.getElementsByTagName("head")[0]).appendChild(a);b=document.styleSheets;this.styleSheet=c=b[b.length-1]}return c},applyRules:function(j){var h=this.getStyleSheet(),l=this.ruleStylesCache,k=h.cssRules,c,e,i,b,d,a,g;for(c in j){e=j[c];i=l[c];if(i===undefined){d=k.length;h.insertRule(c+"{}",d);i=l[c]=k.item(d).style}b=i.$cache;if(!b){b=i.$cache={}}for(a in e){g=this.formatValue(e[a],a);a=this.formatName(a);if(b[a]!==g){b[a]=g;if(g===null){i.removeProperty(a)}else{i.setProperty(a,g,"important")}}}}return this},applyStyles:function(d){var h,c,g,b,a,e;for(h in d){if(d.hasOwnProperty(h)){c=document.getElementById(h);if(!c){continue}g=c.style;b=d[h];for(a in b){if(b.hasOwnProperty(a)){e=this.formatValue(b[a],a);a=this.formatName(a);if(e===null){g.removeProperty(a)}else{g.setProperty(a,e,"important")}}}}}return this},formatName:function(b){var a=this.formattedNameCache,c=a[b];if(!c){if((Ext.os.is.Tizen||!Ext.feature.has.CssTransformNoPrefix)&&this.prefixedProperties[b]){c=this.vendorPrefix+b}else{c=b}a[b]=c}return c},formatValue:function(k,b){var h=typeof k,m=this.DEFAULT_UNIT_LENGTH,e,a,d,g,c,l,j;if(k===null){return""}if(h=="string"){if(this.lengthProperties[b]){j=k.match(this.lengthUnitRegex)[1];if(j.length>0){}else{return k+m}}return k}else{if(h=="number"){if(k==0){return"0"}if(this.lengthProperties[b]){return k+m}if(this.angleProperties[b]){return k+this.DEFAULT_UNIT_ANGLE}if(this.durationProperties[b]){return k+this.DEFAULT_UNIT_DURATION}}else{if(b==="transform"){e=this.transformMethods;c=[];for(d=0,g=e.length;d0)?l.join(", "):"none"}}}}return k}},1,0,0,0,0,0,[Ext.fx.runner,"Css"],0));(Ext.cmd.derive("Ext.fx.runner.CssTransition",Ext.fx.runner.Css,{alternateClassName:"Ext.Animator",singleton:true,listenersAttached:false,constructor:function(){this.runningAnimationsData={};return this.callParent(arguments)},attachListeners:function(){this.listenersAttached=true;Ext.getWin().on("transitionend","onTransitionEnd",this)},onTransitionEnd:function(b){var a=b.target,c=a.id;if(c&&this.runningAnimationsData.hasOwnProperty(c)){this.refreshRunningAnimationsData(Ext.get(a),[b.browserEvent.propertyName])}},onAnimationEnd:function(h,g,d,k,o){var c=h.getId(),l=this.runningAnimationsData[c],p={},n={},b,j,e,m,a;d.un("stop","onAnimationStop",this);if(l){b=l.nameMap}p[c]=n;if(g.onBeforeEnd){g.onBeforeEnd.call(g.scope||this,h,k)}d.fireEvent("animationbeforeend",d,h,k);this.fireEvent("animationbeforeend",this,d,h,k);if(o||(!k&&!g.preserveEndState)){j=g.toPropertyNames;for(e=0,m=j.length;e0},refreshRunningAnimationsData:function(d,l,u,q){var h=d.getId(),r=this.runningAnimationsData,a=r[h];if(!a){return}var n=a.nameMap,t=a.nameList,b=a.sessions,g,k,e,v,m,c,s,p,o=false;u=Boolean(u);q=Boolean(q);if(!b){return this}g=b.length;if(g===0){return this}if(q){a.nameMap={};t.length=0;for(m=0;m");d.close();this.testElement=c=d.createElement("div");c.style.setProperty("position","absolute","important");d.body.appendChild(c);this.testElementComputedStyle=window.getComputedStyle(c)}return c},getCssStyleValue:function(b,e){var d=this.getTestElement(),a=this.testElementComputedStyle,c=d.style;c.setProperty(b,e);if(Ext.browser.is.Firefox){d.offsetHeight}e=a.getPropertyValue(b);c.removeProperty(b);return e},run:function(r){var H=this,k=H.lengthProperties,z={},G={},I={},d,u,A,e,w,K,x,s,t,a,o,C,B,q,D,m,v,h,E,J,l,g,y,p,c,F,b,n;if(!H.listenersAttached){H.attachListeners()}r=Ext.Array.from(r);for(C=0,q=r.length;C0){H.refreshRunningAnimationsData(d,Ext.Array.merge(K,x),true,I.replacePrevious)}c=a.nameMap;F=a.nameList;v={};for(B=0;B0){K=Ext.Array.difference(F,K);x=Ext.Array.merge(K,x);o["transition-property"]=K}z[u]=o;G[u]=Ext.apply({},e);G[u]["transition-property"]=x;G[u]["transition-duration"]=I.duration;G[u]["transition-timing-function"]=I.easing;G[u]["transition-delay"]=I.delay;D.startTime=Date.now()}t=H.$className;H.applyStyles(z);s=function(i){if(i.data===t&&i.source===window){window.removeEventListener("message",s,false);H.applyStyles(G)}};if(window.requestAnimationFrame){window.requestAnimationFrame(function(){window.addEventListener("message",s,false);window.postMessage(t,"*")})}else{Ext.defer(function(){window.addEventListener("message",s,false);window.postMessage(t,"*")},1)}},onAnimationStop:function(d){var g=this.runningAnimationsData,j,a,h,b,c,e;for(j in g){if(g.hasOwnProperty(j)){a=g[j];h=a.sessions;for(b=0,c=h.length;b=h){this.isEnded=true;return a}return g}},0,0,0,0,["easing.ease-in"],0,[Ext.fx.easing,"EaseIn"],0));(Ext.cmd.derive("Ext.fx.easing.Easing",Ext.Base,{constructor:function(a){return Ext.factory(a,Ext.fx.easing.Linear,null,"easing")}},1,0,0,0,0,0,[Ext.fx.easing,"Easing"],0));(Ext.cmd.derive("Ext.fx.layout.card.Abstract",Ext.Evented,{isAnimation:true,config:{direction:"left",duration:null,reverse:null,layout:null},updateLayout:function(a){if(a){this.enable()}},enable:function(){var a=this.getLayout();if(a){a.on("beforeactiveitemchange","onActiveItemChange",this)}},disable:function(){var a=this.getLayout();if(this.isAnimating){this.stopAnimation()}if(a){a.un("beforeactiveitemchange","onActiveItemChange",this)}},onActiveItemChange:Ext.emptyFn,destroy:function(){var b=this,a=b.getLayout();if(b.isAnimating){b.stopAnimation()}if(a){a.un("beforeactiveitemchange","onActiveItemChange",this)}b.setLayout(null);if(b.observableId){b.fireEvent("destroy",this)}Ext.Evented.prototype.destroy.call(this)}},0,0,0,0,0,0,[Ext.fx.layout.card,"Abstract"],0));(Ext.cmd.derive("Ext.fx.layout.card.Style",Ext.fx.layout.card.Abstract,{config:{inAnimation:{before:{visibility:null},preserveEndState:false,replacePrevious:true},outAnimation:{preserveEndState:false,replacePrevious:true}},constructor:function(b){var c,a;Ext.fx.layout.card.Abstract.prototype.constructor.call(this,b);this.endAnimationCounter=0;c=this.getInAnimation();a=this.getOutAnimation();c.on("animationend","incrementEnd",this);a.on("animationend","incrementEnd",this)},updateDirection:function(a){this.getInAnimation().setDirection(a);this.getOutAnimation().setDirection(a)},updateDuration:function(a){this.getInAnimation().setDuration(a);this.getOutAnimation().setDuration(a)},updateReverse:function(a){this.getInAnimation().setReverse(a);this.getOutAnimation().setReverse(a)},incrementEnd:function(){this.endAnimationCounter++;if(this.endAnimationCounter>1){this.endAnimationCounter=0;this.fireEvent("animationend",this)}},applyInAnimation:function(b,a){return Ext.factory(b,Ext.fx.Animation,a)},applyOutAnimation:function(b,a){return Ext.factory(b,Ext.fx.Animation,a)},updateInAnimation:function(a){a.setScope(this)},updateOutAnimation:function(a){a.setScope(this)},onActiveItemChange:function(g,d,i,b){var e=this.getInAnimation(),c=this.getOutAnimation(),h,a;if(d&&i&&i.isPainted()){h=d.renderElement;a=i.renderElement;e.setElement(h);c.setElement(a);c.setOnEnd(function(){b.resume()});h.dom.style.setProperty("visibility","hidden","important");d.show();Ext.Animator.run([c,e]);b.pause()}},destroy:function(){Ext.destroy(this.getInAnimation(),this.getOutAnimation());Ext.fx.layout.card.Abstract.prototype.destroy.call(this)}},1,0,0,0,0,0,[Ext.fx.layout.card,"Style"],0));(Ext.cmd.derive("Ext.fx.layout.card.Slide",Ext.fx.layout.card.Style,{config:{inAnimation:{type:"slide",easing:"ease-out"},outAnimation:{type:"slide",easing:"ease-out",out:true}},updateReverse:function(a){this.getInAnimation().setReverse(a);this.getOutAnimation().setReverse(a)}},0,0,0,0,["fx.layout.card.slide"],0,[Ext.fx.layout.card,"Slide"],0));(Ext.cmd.derive("Ext.fx.layout.card.Cover",Ext.fx.layout.card.Style,{config:{reverse:null,inAnimation:{before:{"z-index":100},after:{"z-index":0},type:"slide",easing:"ease-out"},outAnimation:{easing:"ease-out",from:{opacity:0.99},to:{opacity:1},out:true}},updateReverse:function(a){this.getInAnimation().setReverse(a);this.getOutAnimation().setReverse(a)}},0,0,0,0,["fx.layout.card.cover"],0,[Ext.fx.layout.card,"Cover"],0));(Ext.cmd.derive("Ext.fx.layout.card.Reveal",Ext.fx.layout.card.Style,{config:{inAnimation:{easing:"ease-out",from:{opacity:0.99},to:{opacity:1}},outAnimation:{before:{"z-index":100},after:{"z-index":0},type:"slide",easing:"ease-out",out:true}},updateReverse:function(a){this.getInAnimation().setReverse(a);this.getOutAnimation().setReverse(a)}},0,0,0,0,["fx.layout.card.reveal"],0,[Ext.fx.layout.card,"Reveal"],0));(Ext.cmd.derive("Ext.fx.layout.card.Fade",Ext.fx.layout.card.Style,{config:{reverse:null,inAnimation:{type:"fade",easing:"ease-out"},outAnimation:{type:"fade",easing:"ease-out",out:true}}},0,0,0,0,["fx.layout.card.fade"],0,[Ext.fx.layout.card,"Fade"],0));(Ext.cmd.derive("Ext.fx.layout.card.Flip",Ext.fx.layout.card.Style,{config:{duration:500,inAnimation:{type:"flip",half:true,easing:"ease-out",before:{"backface-visibility":"hidden"},after:{"backface-visibility":null}},outAnimation:{type:"flip",half:true,easing:"ease-in",before:{"backface-visibility":"hidden"},after:{"backface-visibility":null},out:true}},onActiveItemChange:function(d,b,e,a){var c=b.element.getParent();c.addCls("x-layout-card-perspective");this.on("animationend",function(){c.removeCls("x-layout-card-perspective")},this,{single:true});Ext.fx.layout.card.Style.prototype.onActiveItemChange.apply(this,arguments)},updateDuration:function(d){var c=d/2,b=this.getInAnimation(),a=this.getOutAnimation();b.setDelay(c);b.setDuration(c);a.setDuration(c)}},0,0,0,0,["fx.layout.card.flip"],0,[Ext.fx.layout.card,"Flip"],0));(Ext.cmd.derive("Ext.fx.layout.card.Pop",Ext.fx.layout.card.Style,{config:{duration:500,inAnimation:{type:"pop",easing:"ease-out"},outAnimation:{type:"pop",easing:"ease-in",out:true}},updateDuration:function(d){var c=d/2,b=this.getInAnimation(),a=this.getOutAnimation();b.setDelay(c);b.setDuration(c);a.setDuration(c)}},0,0,0,0,["fx.layout.card.pop"],0,[Ext.fx.layout.card,"Pop"],0));(Ext.cmd.derive("Ext.fx.layout.card.Scroll",Ext.fx.layout.card.Abstract,{config:{duration:150},constructor:function(a){this.initConfig(a)},getEasing:function(){var a=this.easing;if(!a){this.easing=a=new Ext.fx.easing.Linear()}return a},updateDuration:function(a){this.getEasing().setDuration(a)},onActiveItemChange:function(a,d,m,c){var j=this.getDirection(),h=this.getEasing(),l,e,b,i,k,g;if(d&&m){if(this.isAnimating){this.stopAnimation()}d.setWidth("100%");d.setHeight("100%");l=this.getLayout().container.innerElement;i=l.getWidth();k=l.getHeight();e=d.renderElement;b=m.renderElement;this.oldItem=m;this.newItem=d;this.containerElement=l;this.currentEventController=c;this.isReverse=g=this.getReverse();d.show();if(j=="right"){j="left";this.isReverse=g=!g}else{if(j=="down"){j="up";this.isReverse=g=!g}}if(j=="left"){if(g){h.setConfig({startValue:i,endValue:0});l.dom.scrollLeft=i;b.setLeft(i)}else{h.setConfig({startValue:0,endValue:i});e.setLeft(i)}}else{if(g){h.setConfig({startValue:k,endValue:0});l.dom.scrollTop=k;b.setTop(k)}else{h.setConfig({startValue:0,endValue:k});e.setTop(k)}}this.startAnimation();c.pause()}},startAnimation:function(){this.isAnimating=true;this.getEasing().setStartTime(Date.now());Ext.AnimationQueue.start(this.doAnimationFrame,this)},doAnimationFrame:function(){var d=this.getEasing(),c=this.getDirection(),a="scrollTop",b;if(c=="left"||c=="right"){a="scrollLeft"}if(d.isEnded){this.stopAnimation()}else{b=d.getValue();this.containerElement.dom[a]=b}},stopAnimation:function(){var c=this,e=c.getDirection(),a="setTop",d=c.oldItem,b=c.newItem;if(e=="left"||e=="right"){a="setLeft"}c.currentEventController.resume();if(c.isReverse&&d&&d.renderElement&&d.renderElement.dom){d.renderElement[a](null)}else{if(b&&b.renderElement&&b.renderElement.dom){b.renderElement[a](null)}}Ext.AnimationQueue.stop(this.doAnimationFrame,this);c.isAnimating=false;c.fireEvent("animationend",c)}},1,0,0,0,["fx.layout.card.scroll"],0,[Ext.fx.layout.card,"Scroll"],0));(Ext.cmd.derive("Ext.fx.layout.Card",Ext.Base,{constructor:function(b){var a=Ext.fx.layout.card.Abstract,c;if(!b){return null}if(typeof b=="string"){c=b;b={}}else{if(b.type){c=b.type}}b.elementBox=false;if(c){a=Ext.ClassManager.getByAlias("fx.layout.card."+c)}return Ext.factory(b,a)}},1,0,0,0,0,0,[Ext.fx.layout,"Card"],0));(Ext.cmd.derive("Ext.fx.layout.card.Cube",Ext.fx.layout.card.Style,{config:{reverse:null,inAnimation:{type:"cube"},outAnimation:{type:"cube",out:true}}},0,0,0,0,["fx.layout.card.cube"],0,[Ext.fx.layout.card,"Cube"],0));(Ext.cmd.derive("Ext.fx.layout.card.ScrollCover",Ext.fx.layout.card.Scroll,{onActiveItemChange:function(c,h,d,e){var i,a,k,j,b,g;this.currentEventController=e;this.inItem=h;if(h&&d){i=this.getLayout().container.innerElement;a=i.getSize();k=this.calculateXY(a);j={easing:this.getEasing(),duration:this.getDuration()};h.renderElement.dom.style.setProperty("visibility","hidden","important");b=h.setTranslatable(true).getTranslatable();g=d.setTranslatable(true).getTranslatable();g.translate({x:0,y:0});b.translate({x:k.left,y:k.top});b.getWrapper().dom.style.setProperty("z-index","100","important");h.show();b.on({animationstart:"onInAnimationStart",animationend:"onInAnimationEnd",scope:this});b.translateAnimated({x:0,y:0},j);e.pause()}},onInAnimationStart:function(){this.inItem.renderElement.dom.style.removeProperty("visibility")},onInAnimationEnd:function(){this.inItem.getTranslatable().getWrapper().dom.style.removeProperty("z-index");this.currentEventController.resume()}},0,0,0,0,["fx.layout.card.scrollcover"],0,[Ext.fx.layout.card,"ScrollCover"],0));(Ext.cmd.derive("Ext.fx.layout.card.ScrollReveal",Ext.fx.layout.card.Scroll,{onActiveItemChange:function(c,h,d,e){var i,a,k,j,g,b;this.currentEventController=e;this.outItem=d;this.inItem=h;if(h&&d){i=this.getLayout().container.innerElement;a=i.getSize();k=this.calculateXY(a);j={easing:this.getEasing(),duration:this.getDuration()};g=d.setTranslatable(true).getTranslatable();b=h.setTranslatable(true).getTranslatable();g.getWrapper().dom.style.setProperty("z-index","100","important");g.translate({x:0,y:0});b.translate({x:0,y:0});h.show();g.on({animationend:"onOutAnimationEnd",scope:this});g.translateAnimated({x:k.x,y:k.y},j);e.pause()}},onOutAnimationEnd:function(){this.outItem.getTranslatable().getWrapper().dom.style.removeProperty("z-index");this.currentEventController.resume()}},0,0,0,0,["fx.layout.card.scrollreveal"],0,[Ext.fx.layout.card,"ScrollReveal"],0));(Ext.cmd.derive("Ext.fx.runner.CssAnimation",Ext.fx.runner.Css,{constructor:function(){this.runningAnimationsMap={};this.elementEndStates={};this.animationElementMap={};this.keyframesRulesCache={};this.uniqueId=0;return Ext.fx.runner.Css.prototype.constructor.apply(this,arguments)},attachListeners:function(){this.listenersAttached=true;Ext.getWin().on({animationstart:"onAnimationStart",animationend:"onAnimationEnd",scope:this})},onAnimationStart:function(h){var b=h.browserEvent.animationName,a=this.animationElementMap[b],g=this.runningAnimationsMap[a][b],i=this.elementEndStates,c=i[a],d={};if(c){delete i[a];d[a]=c;this.applyStyles(d)}if(g.before){d[a]=g.before;this.applyStyles(d)}},onAnimationEnd:function(j){var c=j.target,b=j.browserEvent.animationName,d=this.animationElementMap,a=d[b],g=this.runningAnimationsMap,i=g[a],h=i[b];if(h.onBeforeEnd){h.onBeforeEnd.call(h.scope||this,c)}if(h.onEnd){h.onEnd.call(h.scope||this,c)}delete d[b];delete i[b];this.removeKeyframesRule(b)},generateAnimationId:function(){return"animation-"+(++this.uniqueId)},run:function(g){var t={},u=this.elementEndStates,p=this.animationElementMap,s=this.runningAnimationsMap,b,d,j,l,q,h,r,v,n,m,c,e,a,k,o;if(!this.listenersAttached){this.attachListeners()}g=Ext.Array.from(g);for(q=0,h=g.length;q1)},updateRowCls:function(b,a){this.rowElement.replaceCls(a,b)},updateSelected:function(c,e){var d=this,a=d.selectedCls,b=d.getToolElement();Ext.list.AbstractTreeItem.prototype.updateSelected.call(this,c,e);d.element.toggleCls(a,c);if(b){b.toggleCls(a,c)}},updateSelectedParent:function(b){var c=this;c.element.toggleCls(c.selectedParentCls,b);var a=c.getToolElement();if(a){a.toggleCls(c.selectedCls,b)}},updateText:function(a){this.textElement.update(a)},privates:{doNodeUpdate:function(a){Ext.list.AbstractTreeItem.prototype.doNodeUpdate.call(this,a);this.setRowCls(a&&a.data[this.rowClsProperty])},doIconCls:function(c,b,a){if(a){c.removeCls(a)}if(b){c.addCls(b)}},syncIndent:function(){var c=this,a=c.getIndent(),b=c.getNode(),d;if(b){d=b.data.depth-1;c.wrapElement.dom.style.marginLeft=(d*a)+"px"}},updateExpandCls:function(){if(!this.updatingExpandCls){var e=this,c=e.getExpandable(),d=e.element,b=e.getExpanded(),a=e.expandedCls,g=e.collapsedCls;e.updatingExpandCls=true;d.toggleCls(e.expandableCls,c);if(c){d.toggleCls(a,b);d.toggleCls(g,!b)}else{d.removeCls([a,g])}e.updatingExpandCls=false}},updateIndent:function(b,a){this.syncIndent();Ext.list.AbstractTreeItem.prototype.updateIndent.call(this,b,a)}}},1,["treelistitem"],["widget","treelistitem"],{widget:true,treelistitem:true},["widget.treelistitem"],0,[Ext.list,"TreeItem"],0));Ext.define("Ext.overrides.list.TreeItem",{override:"Ext.list.TreeItem",createFloater:function(){var d=this,a=d.getOwner(),e=a.getUi(),b="x-treelist",c;if(e){b+=" "+b+"-"+e}d.floater=c=new Ext.container.Container({cls:b+" x-treelist-floater",floating:true,width:200,shadow:false,renderTo:Ext.getBody(),listeners:{element:"el",click:function(g){return a.onClick(g)}}});c.add(d);c.show();c.el.alignTo(d.getToolElement(),"tr?");return c},runAnimation:function(a){return this.itemContainer.addAnimation(a)},stopAnimation:function(a){a.jumpToEnd()}});(Ext.cmd.derive("Ext.list.Tree",Ext.Widget,{expanderFirstCls:"x-treelist-expander-first",expanderOnlyCls:"x-treelist-expander-only",highlightPathCls:"x-treelist-highlight-path",microCls:"x-treelist-micro",uiPrefix:"x-treelist-",element:{reference:"element",cls:"x-treelist x-unselectable",listeners:{click:"onClick",mouseenter:"onMouseEnter",mouseleave:"onMouseLeave",mouseover:"onMouseOver"},children:[{reference:"toolsElement",cls:"x-treelist-toolstrip",listeners:{click:"onToolStripClick",mouseover:"onToolStripMouseOver"}}]},cachedConfig:{animation:{duration:500,easing:"ease"},expanderFirst:true,expanderOnly:true},config:{defaults:{xtype:"treelistitem"},highlightPath:null,iconSize:null,indent:null,micro:null,overItem:null,selection:null,selectOnExpander:false,singleExpand:null,store:null,ui:null},twoWayBindable:{selection:1},publishes:{selection:1},defaultBindProperty:"store",constructor:function(a){Ext.Widget.prototype.constructor.call(this,a);this.publishState("selection",this.getSelection())},beforeLayout:function(){this.syncIconSize()},destroy:function(){var a=this;a.destroying=true;a.unfloatAll();a.activeFloater=null;a.setSelection(null);a.setStore(null);Ext.Widget.prototype.destroy.call(this)},updateOverItem:function(g,a){var e={},d=2,h,b;for(h=g;h;h=this.getItem(b.parentNode)){b=h.getNode();e[b.internalId]=true;h.setOver(d);d=1}if(a){for(h=a;h;h=this.getItem(b.parentNode)){b=h.getNode();if(e[b.internalId]){break}h.setOver(0)}}},applySelection:function(c,a){var b=this.getStore();if(!b){c=null}if(c&&c.get("selectable")===false){c=a}return c},updateSelection:function(b,a){var d=this,c;if(!d.destroying){c=d.getItem(a);if(c){c.setSelected(false)}c=d.getItem(b);if(c){c.setSelected(true)}d.fireEvent("selectionchange",d,b)}},applyStore:function(a){return a&&Ext.StoreManager.lookup(a,"tree")},updateStore:function(b,d){var c=this,a;if(d){if(d.getAutoDestroy()){d.destroy()}else{c.storeListeners.destroy()}c.removeRoot();c.storeListeners=null}if(b){c.storeListeners=b.on({destroyable:true,scope:c,nodeappend:c.onNodeAppend,nodecollapse:c.onNodeCollapse,nodeexpand:c.onNodeExpand,nodeinsert:c.onNodeInsert,noderemove:c.onNodeRemove,rootchange:c.onRootChange,update:c.onNodeUpdate});a=b.getRoot();if(a){c.createRootItem(a)}}if(!c.destroying){c.updateLayout()}},updateExpanderFirst:function(a){this.element.toggleCls(this.expanderFirstCls,a)},updateExpanderOnly:function(a){this.element.toggleCls(this.expanderOnlyCls,!a)},updateHighlightPath:function(a){this.element.toggleCls(this.highlightPathCls,a)},updateMicro:function(a){var b=this;if(!a){b.unfloatAll();b.activeFloater=null}b.element.toggleCls(b.microCls,a)},updateUi:function(d,a){var c=this.element,b=this.uiPrefix;if(a){c.removeCls(b+a)}if(d){c.addCls(b+d)}delete this.iconSize;this.syncIconSize()},getItem:function(b){var c=this.itemMap,a;if(b&&c){a=c[b.internalId]}return a||null},getItemConfig:function(b,a){return Ext.apply({parentItem:a.isRootListItem?null:a,owner:this,node:b,indent:this.getIndent()},this.getDefaults())},privates:{checkForOutsideClick:function(b){var a=this.activeFloater;if(!a.element.contains(b.target)){this.unfloatAll()}},collapsingForExpand:false,createItem:function(d,b){var c=Ext.create(this.getItemConfig(d,b)),a;if(b.isRootListItem){a=c.getToolElement();if(a){this.toolsElement.appendChild(a);a.dom.setAttribute("data-recordId",d.internalId);a.isTool=true}}return(this.itemMap[d.internalId]=c)},createRootItem:function(a){var c=this,b;c.itemMap={};c.rootItem=b=new Ext.list.RootTreeItem({indent:c.getIndent(),node:a,owner:c});c.element.appendChild(b.element);c.itemMap[a.internalId]=b},floatItem:function(d,b){var c=this,a;if(d.getFloated()){return}c.unfloatAll();c.activeFloater=a=d;c.floatedByHover=b;d.setFloated(true);if(b){d.getToolElement().on("mouseleave",c.checkForMouseLeave,c);a.element.on("mouseleave",c.checkForMouseLeave,c)}else{Ext.on("mousedown",c.checkForOutsideClick,c)}},onClick:function(b){var a=b.getTarget("[data-recordId]"),c;if(a){c=a.getAttribute("data-recordId");a=this.itemMap[c];if(a){a.onClick(b)}}},onMouseEnter:function(a){this.onMouseOver(a)},onMouseLeave:function(){this.setOverItem(null)},onMouseOver:function(b){var a=Ext.Component.fromElement(b.getTarget());this.setOverItem(a&&a.isTreeListItem&&a)},checkForMouseLeave:function(c){var b=this.activeFloater,a=c.getRelatedTarget();if(b){if(a!==b.getToolElement().dom&&!b.element.contains(a)){this.unfloatAll()}}},onNodeAppend:function(a,c){if(a){var b=this.itemMap[a.internalId];if(b){b.nodeInsert(c,null)}}},onNodeCollapse:function(b){var a=this.itemMap[b.internalId];if(a){a.nodeCollapse(b,this.collapsingForExpand)}},onNodeExpand:function(g){var e=this,d=e.itemMap[g.internalId],h,b,c,a,j;if(d){if(!d.isRootItem&&e.getSingleExpand()){e.collapsingForExpand=true;a=(d.getParentItem()||e.rootItem).getNode();h=a.childNodes;for(c=0,b=h.length;c"){this.isParentReference=true;b=b.substring(0,a)}return b}}},0,0,0,0,0,0,[Ext.mixin,"Container"],0));(Ext.cmd.derive("Ext.mixin.Hookable",Ext.Mixin,{mixinConfig:{id:"hookable"},bindHook:function(a,d,i,c,b){if(!i){i=d}var g=a[d],e,h;if(g&&g.hasOwnProperty("$binding")){h=g.$binding;if(h.bindingMethod===i&&h.bindingScope===this){return this}}a[d]=e=function(){var l=e.$binding,k=l.bindingScope,j=Array.prototype.slice.call(arguments);j.push(arguments);if(b){j.push.apply(j,b)}if(!l.preventDefault&&k[l.bindingMethod].apply(k,j)!==false){return l.boundFn.apply(this,arguments)}};e.$binding={preventDefault:!!c,boundFn:g,bindingMethod:i,bindingScope:this};return this},unbindHook:function(a,b,h){if(!h){h=b}var d=a[b],e=d.$binding,c,g;while(e){c=e.boundFn;if(e.bindingMethod===h&&e.bindingScope===this){if(g){g.boundFn=c}else{a[b]=c}return this}g=e;e=c.$binding}return this}},0,0,0,0,0,0,[Ext.mixin,"Hookable"],0));Ext.define("Ext.mixin.Mashup",function(a){return{extend:"Ext.Mixin",mixinConfig:{id:"mashup",extended:function(c,b){a.process(b)}},statics:{process:function(e){var c=e.prototype,g=c.requiredScripts,b=e._classHooks,d=b.onCreated;if(g){delete c.requiredScripts;b.onCreated=function(){var i=this,h=Ext.Array.slice(arguments);Ext.Loader.loadScripts({url:g,cache:true,onLoad:function(){b.onCreated=d;b.onCreated.call(i,h)}})}}}},onClassMixedIn:function(b){a.process(b)}}});(Ext.cmd.derive("Ext.mixin.Responsive",Ext.Mixin,function(a){return{mixinConfig:{id:"responsive",after:{destroy:"destroy"}},config:{responsiveConfig:{$value:undefined,merge:function(h,c,g,e){if(!h){return c}var b=c?Ext.Object.chain(c):{},d;for(d in h){if(!e||!(d in b)){b[d]={fn:null,config:h[d]}}}return b}},responsiveFormulas:{$value:0,merge:function(e,b,d,c){return this.mergeNew(e,b,d,c)}}},destroy:function(){a.unregister(this);this.callParent()},privates:{statics:{active:false,all:{},context:Ext.Object.chain(Ext.platformTags),count:0,nextId:0,activate:function(){a.active=true;a.updateContext();Ext.on("resize",a.onResize,a)},deactivate:function(){a.active=false;Ext.un("resize",a.onResize,a)},notify:function(){var d=a.all,c=a.context,b=Ext.GlobalEvents,g=a.timer,e;if(g){a.timer=null;Ext.Function.cancelAnimationFrame(g)}a.updateContext();Ext.suspendLayouts();b.fireEvent("beforeresponsiveupdate",c);for(e in d){d[e].setupResponsiveContext()}b.fireEvent("beginresponsiveupdate",c);for(e in d){d[e].updateResponsiveState()}b.fireEvent("responsiveupdate",c);Ext.resumeLayouts(true)},onResize:function(){if(!a.timer){a.timer=Ext.Function.requestAnimationFrame(a.onTimer)}},onTimer:function(){a.timer=null;a.notify()},processConfig:function(b,i,g){var h=i&&i[g],e=b.config,c,d;if(h){d=b.getConfigurator();c=d.configs[g];e[g]=c.merge(h,e[g],b)}},register:function(b){var c=b.$responsiveId;if(!c){b.$responsiveId=c=++a.nextId;a.all[c]=b;if(++a.count===1){a.activate()}}},unregister:function(b){var c=b.$responsiveId;if(c in a.all){b.$responsiveId=null;delete a.all[c];if(--a.count===0){a.deactivate()}}},updateContext:function(){var e=Ext.Element,d=e.getViewportWidth(),b=e.getViewportHeight(),c=a.context;c.width=d;c.height=b;c.tall=dh){e=h;h=c;c=e}for(d=c;d<=h;d++){a.push(b.getAt(d))}this.doMultiSelect(a,j)},select:function(c,e,b){var d=this,a;if(d.getDisableSelection()){return}if(typeof c==="number"){c=[d.getStore().getAt(c)]}if(!c){return}if(d.getMode()=="SINGLE"&&c){a=c.length?c[0]:c;d.doSingleSelect(a,b)}else{d.doMultiSelect(c,e,b)}},doSingleSelect:function(a,b){var d=this,c=d.selected;if(d.getDisableSelection()){return}if(d.isSelected(a)){return}if(c.getCount()>0){d.deselect(d.getLastSelected(),b)}c.add(a);d.setLastSelected(a);d.onItemSelect(a,b);d.setLastFocused(a);if(!b){d.fireSelectionChange([a])}},doMultiSelect:function(a,k,j){if(a===null||this.getDisableSelection()){return}a=!Ext.isArray(a)?[a]:a;var g=this,b=g.selected,e=a.length,h=false,c=0,d;if(!k&&b.getCount()>0){h=true;g.deselect(g.getSelections(),true)}for(;c0},refreshSelection:function(){var b=this,a=b.getSelections();b.deselectAll(true);if(a.length){b.select(a,false,true)}},onSelectionStoreRemove:function(c,b){var h=this,e=h.selected,g=b.length,j,a,d;if(h.getDisableSelection()){return}for(d=0;d',''," ({childCount} children)","",''," ({depth} deep)","",'',", {type}: {[this.time(values.sum)]} msec (","avg={[this.time(values.sum / parent.count)]}",")","","
    "].join(""),{time:function(m){return Math.round(m*100)/100}})}var l=this.getData(k);l.name=this.name;l.pure.type="Pure";l.total.type="Total";l.times=[l.pure,l.total];return d.apply(l)},getData:function(k){var l=this;return{count:l.count,childCount:l.childCount,depth:l.maxDepth,pure:g(l.count,l.childCount,k,l.pure),total:g(l.count,l.childCount,k,l.total)}},enter:function(){var k=this,l={accum:k,leave:e,childTime:0,parent:c};++k.depth;if(k.maxDepth0){k=i==="this"?this:typeof i==="string"?this[i]:typeof i==="number"?arguments[i]:null;if(k){n=k.id}if(n!=null){console.log(h+" for "+n+": "+m+"ms")}else{console.log(h+" for unknown: "+m+"ms")}if(console.trace){console.trace()}}return j}})(g,b[g])}Ext.override(a,d)}}}}},1,0,0,0,0,0,[Ext.perf,"Monitor",Ext,"Perf"],0));(Ext.cmd.derive("Ext.plugin.Abstract",Ext.Base,{alternateClassName:"Ext.AbstractPlugin",isPlugin:true,constructor:function(a){if(a){this.pluginConfig=a;this.initConfig(a)}},clonePlugin:function(a){return new this.self(Ext.apply({},a,this.pluginConfig))},setCmp:function(a){this.cmp=a},getCmp:function(){return this.cmp},init:Ext.emptyFn,destroy:function(){this.cmp=this.pluginConfig=null;this.callParent()},onClassExtended:function(b,d,a){var c=d.alias;if(c&&!d.ptype){if(Ext.isArray(c)){c=c[0]}b.prototype.ptype=c.split("plugin.")[1]}},resolveListenerScope:function(d){var c=this,b=c.getCmp(),a;if(b){a=b.resolveSatelliteListenerScope(c,d)}return a||c.mixins.observable.resolveListenerScope.call(c,d)}},1,0,0,0,0,0,[Ext.plugin,"Abstract",Ext,"AbstractPlugin"],0));Ext.define("Ext.overrides.plugin.Abstract",{override:"Ext.plugin.Abstract",$configStrict:false,$configPrefixed:false,disabled:false,getState:null,applyState:null,enable:function(){this.disabled=false},disable:function(){this.disabled=true}});(Ext.cmd.derive("Ext.plugin.LazyItems",Ext.plugin.Abstract,{init:function(a){Ext.plugin.Abstract.prototype.init.apply(this,arguments);if(this.items){if(this.eagerInstantiation){this.items=a.prepareItems(this.items)}}a.beforeRender=Ext.Function.createInterceptor(a.beforeRender,this.beforeComponentRender,this)},beforeComponentRender:function(){this.cmp.add(this.items);this.cmp.beforeComponentRender=null}},0,0,0,0,["plugin.lazyitems"],0,[Ext.plugin,"LazyItems"],0));(Ext.cmd.derive("Ext.util.Base64",Ext.Base,{singleton:true,_str:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var d=this;var a="",n,l,j,m,k,h,g,b=0;e=d._utf8_encode(e);var c=e.length;while(b>2;k=((n&3)<<4)|(l>>4);h=((l&15)<<2)|(j>>6);g=j&63;if(isNaN(l)){h=g=64}else{if(isNaN(j)){g=64}}a=a+d._str.charAt(m)+d._str.charAt(k)+d._str.charAt(h)+d._str.charAt(g)}return a},decode:function(e){var d=this;var a="",n,l,j,m,k,h,g,b=0;e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");var c=e.length;while(b>4);l=((k&15)<<4)|(h>>2);j=((h&3)<<6)|g;a=a+String.fromCharCode(n);if(h!==64){a=a+String.fromCharCode(l)}if(g!==64){a=a+String.fromCharCode(j)}}a=d._utf8_decode(a);return a},_utf8_encode:function(d){d=d.replace(/\r\n/g,"\n");var b="",g=0,a=d.length;for(;g127)&&(e<2048)){b+=String.fromCharCode((e>>6)|192);b+=String.fromCharCode((e&63)|128)}else{b+=String.fromCharCode((e>>12)|224);b+=String.fromCharCode(((e>>6)&63)|128);b+=String.fromCharCode((e&63)|128)}}}return b},_utf8_decode:function(b){var e="",h=0,j=0,d=0,g=0,a=b.length;while(h191)&&(j<224)){g=b.charCodeAt(h+1);e+=String.fromCharCode(((j&31)<<6)|(g&63));h+=2}else{g=b.charCodeAt(h+1);d=b.charCodeAt(h+2);e+=String.fromCharCode(((j&15)<<12)|((g&63)<<6)|(d&63));h+=3}}}return e}},0,0,0,0,0,0,[Ext.util,"Base64"],0));(Ext.cmd.derive("Ext.util.DelimitedValue",Ext.Base,{dateFormat:"C",delimiter:"\t",lineBreak:"\n",quote:'"',parseREs:{},quoteREs:{},lineBreakRe:/\r?\n/g,constructor:function(a){if(a){Ext.apply(this,a)}},decode:function(k,b){var i=this,c=(b||i.delimiter),n=[],o=[n],a=i.quote,g=i.quoteREs,l=i.parseREs,m=l[c]||(l[c]=new RegExp("(\\"+c+"|\\r?\\n|\\r|^)(?:\\"+a+"([^\\"+a+"]*(?:\\"+a+"\\"+a+"[^\\"+a+"]*)*)\\"+a+'|([^"\\'+c+"\\r\\n]*))","gi")),e=g[a]||(g[a]=new RegExp("\\"+a+"\\"+a,"g")),h,d,j;while(h=m.exec(k)){d=h[1];if(d.length&&d!==c){o.push(n=[])}if(h[2]){j=h[2].replace(e,'"')}else{j=h[3]}n.push(j)}return o},encode:function(m,c){var j=this,e=c||j.delimiter,d=j.dateFormat,b=j.quote,k=b+b,l=m.length,h=j.lineBreakRe,o=[],a=[],g,i,n;while(l-->0){n=m[l];a.length=i=n.length;while(i-->0){g=n[i];if(g==null){g=""}else{if(typeof g==="string"){if(g){if(g.indexOf(b)>-1){g=b+g.split(b).join(k)+b}else{if(g.indexOf(e)>-1||h.test(g)){g=b+g+b}}}}else{if(Ext.isDate(g)){g=Ext.Date.format(g,d)}}}a[i]=g}o[l]=a.join(e)}return o.join(j.lineBreak)}},1,0,0,0,0,0,[Ext.util,"DelimitedValue"],0));(Ext.cmd.derive("Ext.util.CSV",Ext.util.DelimitedValue,{singleton:true,delimiter:","},0,0,0,0,0,0,[Ext.util,"CSV"],0));(Ext.cmd.derive("Ext.util.ItemCollection",Ext.util.MixedCollection,{alternateClassName:"Ext.ItemCollection",getKey:function(a){return a.getItemId&&a.getItemId()},has:function(a){return this.map.hasOwnProperty(a.getId())}},0,0,0,0,0,0,[Ext.util,"ItemCollection",Ext,"ItemCollection"],0));(Ext.cmd.derive("Ext.util.LocalStorage",Ext.Base,{id:null,destroyed:false,lazyKeys:true,prefix:"",session:false,_keys:null,_store:null,_users:0,statics:{cache:{},get:function(e){var d=this,b=d.cache,c={_users:1},a;if(Ext.isString(e)){c.id=e}else{Ext.apply(c,e)}if(!(a=b[c.id])){a=new d(c)}else{++a._users}return a},supported:true},constructor:function(a){var b=this;Ext.apply(b,a);if(b._users){Ext.util.LocalStorage.cache[b.id]=b}b.init()},init:function(){var a=this,b=a.id;if(!a.prefix&&b){a.prefix=b+"-"}a._store=(a.session?window.sessionStorage:window.localStorage)},destroy:function(){var a=this;delete Ext.util.LocalStorage.cache[a.id];a._store=a._keys=null;a.callParent()},getKeys:function(){var e=this,a=e._store,g=e.prefix,d=e._keys,h=g.length,c,b;if(!d){e._keys=d=[];for(c=a.length;c--;){b=a.key(c);if(b.length>h){if(g===b.substring(0,h)){d.push(b.substring(h))}}}}return d},release:function(){if(!--this._users){this.destroy()}},save:Ext.emptyFn,clear:function(){var d=this,a=d._store,e=d.prefix,c=d._keys||d.getKeys(),b;for(b=c.length;b--;){a.removeItem(e+c[b])}c.length=0},key:function(a){var b=this._keys||this.getKeys();return(0<=a&&a-1&&c','
    role="presentation"','class="{innerCtCls}">',"{%this.renderBody(out,values)%}","
    ",""],beginLayout:function(a){Ext.layout.container.Container.prototype.beginLayout.apply(this,arguments);this.initContextItems(a)},beforeLayoutCycle:function(d){var b=this.owner,c=b.inheritedState,a=b.inheritedStateInner;if(!c||c.invalid){c=b.getInherited();a=b.inheritedStateInner}if(d.widthModel.shrinkWrap){a.inShrinkWrapTable=true}else{delete a.inShrinkWrapTable}},beginLayoutCycle:function(d){var j=this,b=j.outerCt,h=j.lastOuterCtWidth||"",g=j.lastOuterCtHeight||"",k=j.lastOuterCtTableLayout||"",a=d.state,l,e,m,c,i;Ext.layout.container.Container.prototype.beginLayoutCycle.apply(this,arguments);e=m=c="";if(!d.widthModel.shrinkWrap){e="100%";i=j.owner.inheritedStateInner;l=j.getOverflowXStyle(d);c=(i.inShrinkWrapTable||l==="auto"||l==="scroll")?"":"fixed"}if(!d.heightModel.shrinkWrap&&!Ext.supports.PercentageHeightOverflowBug){m="100%"}if((e!==h)||j.hasOuterCtPxWidth){b.setStyle("width",e);j.lastOuterCtWidth=e;j.hasOuterCtPxWidth=false}if(c!==k){b.setStyle("table-layout",c);j.lastOuterCtTableLayout=c}if((m!==g)||j.hasOuterCtPxHeight){b.setStyle("height",m);j.lastOuterCtHeight=m;j.hasOuterCtPxHeight=false}if(j.hasInnerCtPxHeight){j.innerCt.setStyle("height","");j.hasInnerCtPxHeight=false}a.overflowAdjust=a.overflowAdjust||j.lastOverflowAdjust},calculate:function(c){var a=this,b=c.state,e=a.getContainerSize(c,true),d=b.calculatedItems||(b.calculatedItems=a.calculateItems?a.calculateItems(c,e):true);a.setCtSizeIfNeeded(c,e);if(d&&c.hasDomProp("containerChildrenSizeDone")){a.calculateContentSize(c);if(e.gotAll){if(a.manageOverflow&&!c.state.secondPass&&!a.reserveScrollbar){a.calculateOverflow(c,e)}return}}a.done=false},calculateContentSize:function(g){var e=this,a=((g.widthModel.shrinkWrap?1:0)|(g.heightModel.shrinkWrap?2:0)),c=(a&1)||undefined,h=(a&2)||undefined,d=0,b=g.props;if(c){if(isNaN(b.contentWidth)){++d}else{c=undefined}}if(h){if(isNaN(b.contentHeight)){++d}else{h=undefined}}if(d){if(c&&!g.setContentWidth(e.measureContentWidth(g))){e.done=false}if(h&&!g.setContentHeight(e.measureContentHeight(g))){e.done=false}}},calculateOverflow:function(c){var h=this,b,j,a,g,e,d,i;e=(h.getOverflowXStyle(c)==="auto");d=(h.getOverflowYStyle(c)==="auto");if(e||d){a=Ext.getScrollbarSize();i=c.overflowContext.el.dom;g=0;if(i.scrollWidth>i.clientWidth){g|=1}if(i.scrollHeight>i.clientHeight){g|=2}b=(d&&(g&2))?a.width:0;j=(e&&(g&1))?a.height:0;if(b!==h.lastOverflowAdjust.width||j!==h.lastOverflowAdjust.height){h.done=false;c.invalidate({state:{overflowAdjust:{width:b,height:j},overflowState:g,secondPass:true}})}}},completeLayout:function(a){this.lastOverflowAdjust=a.state.overflowAdjust},doRenderBody:function(c,e){var d=e.$layout,a=Ext.XTemplate,g=d.beforeBodyTpl,b=d.afterBodyTpl;if(g){a.getTpl(d,"beforeBodyTpl").applyOut(e,c)}this.renderItems(c,e);this.renderContent(c,e);if(b){a.getTpl(d,"afterBodyTpl").applyOut(e,c)}},doRenderPadding:function(b,d){var c=d.$layout,a=d.$layout.owner,e=a[a.contentPaddingProperty];if(c.managePadding&&e){b.push("padding:",a.unitizeBox(e))}},finishedLayout:function(b){var a=this.innerCt;Ext.layout.container.Container.prototype.finishedLayout.apply(this,arguments);if(Ext.isIE8){a.repaint()}if(Ext.isOpera){a.setStyle("position","relative");a.dom.scrollWidth;a.setStyle("position","")}},getContainerSize:function(b,c){var a=Ext.layout.container.Container.prototype.getContainerSize.apply(this,arguments),d=b.state.overflowAdjust;if(d){a.width-=d.width;a.height-=d.height}return a},getRenderData:function(){var a=this,b=Ext.layout.container.Container.prototype.getRenderData.call(this);b.innerCtCls=a.innerCtCls;b.outerCtCls=a.outerCtCls;return b},getRenderTarget:function(){return this.innerCt},getElementTarget:function(){return this.innerCt},getOverflowXStyle:function(a){return a.overflowXStyle||(a.overflowXStyle=this.owner.scrollFlags.overflowX||a.overflowContext.getStyle("overflow-x"))},getOverflowYStyle:function(a){return a.overflowYStyle||(a.overflowYStyle=this.owner.scrollFlags.overflowY||a.overflowContext.getStyle("overflow-y"))},initContextItems:function(b){var a=this,d=b.target,c=a.owner.getOverflowEl();b.outerCtContext=b.getEl("outerCt",a);b.innerCtContext=b.getEl("innerCt",a);b.overflowContext=(c===b.el)?b:b.getEl(c);if(d[d.contentPaddingProperty]!==undefined){b.paddingContext=b.innerCtContext}},initLayout:function(){var c=this,b=Ext.getScrollbarSize().width,a=c.owner;Ext.layout.container.Container.prototype.initLayout.call(this);if(b&&c.manageOverflow&&!c.hasOwnProperty("lastOverflowAdjust")){if(a.scrollable||c.reserveScrollbar){c.lastOverflowAdjust={width:b,height:0}}}},measureContentHeight:function(b){var a=this.outerCt.getHeight(),c=b.target;if(this.managePadding&&(c[c.contentPaddingProperty]===undefined)){a+=b.targetContext.getPaddingInfo().height}return a},measureContentWidth:function(d){var g,c,b,a,e;if(this.chromeCellMeasureBug){g=this.innerCt.dom;c=g.style;b=c.display;if(b==="table-cell"){c.display="";g.offsetWidth;c.display=b}}if(Ext.isSafari){g=this.outerCt.dom;c=g.style;c.display="table-cell";g.offsetWidth;g.style.display=""}a=this.outerCt.getWidth();e=d.target;if(this.managePadding&&(e[e.contentPaddingProperty]===undefined)){a+=d.targetContext.getPaddingInfo().width}return a},setCtSizeIfNeeded:function(d,b){var e=this,l=b.height,h=d.paddingContext.getPaddingInfo(),j=e.getTarget(),k=e.getOverflowXStyle(d),c=(k==="auto"||k==="scroll"),a=Ext.getScrollbarSize(),g,i;if(l&&!d.heightModel.shrinkWrap){if(Ext.supports.PercentageHeightOverflowBug){g=true}if(Ext.isIE8){i=true}if((g||i)&&c&&(j.dom.scrollWidth>j.dom.clientWidth)){l=Math.max(l-a.height,0)}if(g){d.outerCtContext.setProp("height",l+h.height);e.hasOuterCtPxHeight=true}if(i){d.innerCtContext.setProp("height",l);e.hasInnerCtPxHeight=true}}},setupRenderTpl:function(a){Ext.layout.container.Container.prototype.setupRenderTpl.apply(this,arguments);a.renderPadding=this.doRenderPadding},getContentTarget:function(){return this.innerCt},getScrollerEl:function(){return this.outerCt}},0,0,0,0,["layout.auto","layout.autocontainer"],0,[Ext.layout.container,"Auto"],function(){this.prototype.chromeCellMeasureBug=Ext.isChrome&&Ext.chromeVersion>=26}));(Ext.cmd.derive("Ext.ZIndexManager",Ext.Base,{alternateClassName:"Ext.WindowGroup",statics:{zBase:9000,activeCounter:0},constructor:function(a){var b=this;b.id=Ext.id(null,"zindex-mgr-");b.zIndexStack=new Ext.util.Collection({sorters:{sorterFn:function(e,d){var c=(e.alwaysOnTop||0)-(d.alwaysOnTop||0);if(!c){c=e.getActiveCounter()-d.getActiveCounter()}return c}},filters:{filterFn:function(c){return c.isVisible()}}});b.zIndexStack.addObserver(b);b.front=null;b.globalListeners=Ext.GlobalEvents.on({beforehide:b.onComponentShowHide,show:b.onComponentShowHide,scope:b,destroyable:true});if(a){if(a.isContainer){a.on("resize",b.onContainerResize,b);b.zseed=Ext.Number.from(b.rendered?a.getEl().getStyle("zIndex"):undefined,b.getNextZSeed());b.targetEl=a.getTargetEl();b.container=a}else{Ext.on("resize",b.onContainerResize,b);b.zseed=b.getNextZSeed();b.targetEl=Ext.get(a)}}else{b.zseed=b.getNextZSeed();Ext.onInternalReady(function(){Ext.on("resize",b.onContainerResize,b);b.targetEl=Ext.getBody()})}},getId:function(){return this.id},getNextZSeed:function(){return(Ext.ZIndexManager.zBase+=10000)},setBase:function(a){this.zseed=a;return this.onCollectionSort()},onCollectionSort:function(){var k=this,e=k.front,j=e&&e.containsFocus,l=k.zseed,m=k.zIndexStack.getRange(),h=m.length,d,g,c,b,n=false;for(d=0;d0;){b=a[c];if(b.isComponent&&e.call(d||b,b)===false){return}}},destroy:function(){var d=this,b=d.zIndexStack.getRange(),a=b.length,c;for(c=0;c1){a.refresh()}if(b.hasListeners.afterlayout){b.fireEvent("afterlayout",b,c)}},beforeDestroy:function(){var b=this,a=b.items,d=b.floatingItems,e;if(a){while((e=a.first())){b.doRemove(e,true)}}if(d){while((e=d.first())){b.doRemove(e,true)}}Ext.destroy(b.layout);Ext.Component.prototype.beforeDestroy.call(this)},destroy:function(){var a=this;Ext.Component.prototype.destroy.call(this);if(a.items){a.items.destroy()}if(a.floatingItems){a.floatingItems.destroy()}a.refs=a.items=a.floatingItems=a.layout=null},beforeRender:function(){var b=this,a=b.getLayout(),c;b.preventChildDisable=true;Ext.Component.prototype.beforeRender.call(this);b.preventChildDisable=false;if(!a.initialized){a.initLayout()}c=a.targetCls;if(c){b.applyTargetCls(c)}},cascade:function(l,m,a){var k=this,e=k.items?k.items.items:[],g=e.length,d=0,j,h=a?a.concat(k):[k],b=h.length-1;if(l.apply(m||k,h)!==false){for(;d111&&a.keyCode<124){a.keyCode=-1}}catch(b){}}},getId:function(a){a=Ext.get(a);return a.id},getRelatedTarget:function(a){a=a.browserEvent||a;var b=a.relatedTarget;if(!b){if(this.mouseLeaveRe.test(a.type)){b=a.toElement}else{if(this.mouseEnterRe.test(a.type)){b=a.fromElement}}}return this.resolveTextNode(b)},getPageX:function(a){return this.getPageXY(a)[0]},getPageXY:function(c){c=c.browserEvent||c;var b=c.pageX,e=c.pageY,d=document.documentElement,a=document.body;if(!b&&b!==0){b=c.clientX+(d&&d.scrollLeft||a&&a.scrollLeft||0)-(d&&d.clientLeft||a&&a.clientLeft||0);e=c.clientY+(d&&d.scrollTop||a&&a.scrollTop||0)-(d&&d.clientTop||a&&a.clientTop||0)}return[b,e]},getPageY:function(a){return this.getPageXY(a)[1]},getTarget:function(a){a=a.browserEvent||a;return Ext.EventManager.resolveTextNode(a.target||a.srcElement)},resolveTextNode:Ext.isGecko?function(b){if(b){var a=HTMLElement.prototype.toString.call(b);if(a!=="[xpconnect wrapped native prototype]"&&a!=="[object XULElement]"){return b.nodeType===3?b.parentNode:b}}}:function(a){return a&&a.nodeType===3?a.parentNode:a}},0,0,0,0,0,0,[Ext,"EventManager"],function(a){a.on=a.addListener;a.un=a.removeListener}));(Ext.cmd.derive("Ext.Img",Ext.Component,{autoEl:"img",baseCls:"x-img",config:{src:null},alt:"",title:"",imgCls:"",maskOnDisable:false,initComponent:function(){if(this.glyph){this.autoEl="div"}Ext.Component.prototype.initComponent.call(this)},applySrc:function(a){return a&&Ext.resolveResource(a)},getElConfig:function(){var e=this,h=e.autoEl,b=Ext.Component.prototype.getElConfig.call(this),g=Ext._glyphFontFamily,d=e.glyph,a,c;if(h==="img"||(Ext.isObject(h)&&h.tag==="img")){a=b}else{if(e.glyph){if(typeof d==="string"){c=d.split("@");d=c[0];g=c[1]||g}b.html="&#"+d+";";if(g){b.style=b.style||{};b.style.fontFamily=g}b.role="img"}else{b.cn=[a={tag:"img",id:e.id+"-img"}]}}if(a){if(e.imgCls){a.cls=(a.cls?a.cls+" ":"")+e.imgCls}a.src=e.src||Ext.BLANK_IMAGE_URL}if(e.alt){(a||b).alt=e.alt}else{(a||b).alt=""}if(e.title){(a||b).title=e.title}return b},onRender:function(){var b=this,c=b.autoEl,a;Ext.Component.prototype.onRender.apply(this,arguments);a=b.el;if(c==="img"||(Ext.isObject(c)&&c.tag==="img")){b.imgEl=a}else{b.imgEl=a.getById(b.id+"-img")}},onDestroy:function(){var a=this,b=a.imgEl;if(b&&a.el!==b){b.destroy()}this.imgEl=null;Ext.Component.prototype.onDestroy.call(this)},getTitle:function(){return this.title},setTitle:function(c){var a=this,b=a.imgEl;a.title=c||"";if(b){b.dom.title=c||""}},getAlt:function(){return this.alt},setAlt:function(c){var a=this,b=a.imgEl;a.alt=c||"";if(b){b.dom.alt=c||""}},updateSrc:function(b){var a=this.imgEl;if(a){a.dom.src=b||Ext.BLANK_IMAGE_URL}},setGlyph:function(e){var d=this,g=Ext._glyphFontFamily,a=d.glyph,b=d.el,c;d.glyph=e;if(d.rendered&&e!==a){if(typeof e==="string"){c=e.split("@");e=c[0];g=c[1]||g}b.dom.innerHTML="&#"+e+";";if(g){b.setStyle("font-family",g)}}}},0,["image","imagecomponent"],["component","box","image","imagecomponent"],{component:true,box:true,image:true,imagecomponent:true},["widget.image","widget.imagecomponent"],0,[Ext,"Img"],0));(Ext.cmd.derive("Ext.util.StoreHolder",Ext.Base,{mixinId:"storeholder",bindStore:function(b,c,a){a=a||"store";var d=this,e=c?null:d[a];if(b!==e){if(e){d.onUnbindStore(e,c,a);if(d.isComponent&&a==="store"&&e.autoDestroy){e.destroy()}else{d.unbindStoreListeners(e)}}if(b){d[a]=b=Ext.data.StoreManager.lookup(b);d.bindStoreListeners(b);d.onBindStore(b,e)}else{d[a]=null}}return d},getStore:function(){return this.store},setStore:function(a){this.bindStore(a)},unbindStoreListeners:function(a){var b=this.storeListeners;if(b){a.un(b)}},bindStoreListeners:function(a){var b=this.getStoreListeners(a);if(b){b=Ext.apply({},b);if(!b.scope){b.scope=this}this.storeListeners=b;a.on(b)}},getStoreListeners:Ext.emptyFn,onUnbindStore:Ext.emptyFn,onBindStore:Ext.emptyFn},0,0,0,0,0,0,[Ext.util,"StoreHolder"],0));(Ext.cmd.derive("Ext.LoadMask",Ext.Component,{isLoadMask:true,msg:"Loading...",msgCls:"x-mask-loading",msgWrapCls:"x-mask-msg",useMsg:true,useTargetEl:false,cls:"x-mask",componentCls:"x-border-box",ariaRole:"status",focusable:true,tabIndex:0,autoEl:{tag:"div",role:"status"},childEls:["msgWrapEl","msgEl","msgTextEl"],renderTpl:['
    ','
    ','
    {msg}
    ',"
    ","
    "],maskOnDisable:false,skipLayout:true,constructor:function(b){var c=this,a;if(arguments.length===2){a=c.target=b;b=arguments[1]}else{a=b.target}Ext.Component.prototype.constructor.call(this,b);if(a.isComponent){c.ownerCt=a;c.hidden=true;c.renderTo=c.getMaskTarget();c.external=c.renderTo===Ext.getBody();c.bindComponent(a)}else{a=Ext.get(a);c.isElement=true;c.renderTo=c.target}c.render(c.renderTo);if(c.store){c.bindStore(c.store,true)}},initRenderData:function(){var a=Ext.Component.prototype.initRenderData.apply(this,arguments);a.msg=this.msg||"";return a},onRender:function(){Ext.Component.prototype.onRender.apply(this,arguments);this.maskEl=this.el},bindComponent:function(a){var c=this,b={scope:this,resize:c.sizeMask};if(c.external){b.added=c.onComponentAdded;b.removed=c.onComponentRemoved;if(a.floating){b.move=c.sizeMask;c.activeOwner=a}else{if(a.ownerCt){c.onComponentAdded(a.ownerCt)}}}c.mon(a,b);if(c.external){c.mon(Ext.GlobalEvents,{show:c.onContainerShow,hide:c.onContainerHide,expand:c.onContainerExpand,collapse:c.onContainerCollapse,scope:c})}},onComponentAdded:function(a){var b=this;delete b.activeOwner;b.floatParent=a;if(!a.floating){a=a.up("[floating]")}if(a){b.activeOwner=a;b.mon(a,"move",b.sizeMask,b);b.mon(a,"tofront",b.onOwnerToFront,b)}else{b.preventBringToFront=true}a=b.floatParent.ownerCt;if(b.rendered&&b.isVisible()&&a){b.floatOwner=a;b.mon(a,"afterlayout",b.sizeMask,b,{single:true})}},onComponentRemoved:function(a){var c=this,d=c.activeOwner,b=c.floatOwner;if(d){c.mun(d,"move",c.sizeMask,c);c.mun(d,"tofront",c.onOwnerToFront,c)}if(b){c.mun(b,"afterlayout",c.sizeMask,c)}delete c.activeOwner;delete c.floatOwner},afterRender:function(){var a=this;Ext.Component.prototype.afterRender.apply(this,arguments);if(Ext.isIE){a.el.on("mousedown",a.onMouseDown,a)}this.el.skipGarbageCollection=true},onMouseDown:function(b){var a=this.el;if(b.within(a)){b.preventDefault();a.focus()}},onOwnerToFront:function(a,b){this.el.setStyle("zIndex",b+1)},onContainerShow:function(a){if(!this.isHierarchicallyHidden()){this.onComponentShow()}},onContainerHide:function(a){if(this.isHierarchicallyHidden()){this.onComponentHide()}},onContainerExpand:function(a){if(!this.isHierarchicallyHidden()){this.onComponentShow()}},onContainerCollapse:function(a){if(this.isHierarchicallyHidden()){this.onComponentHide()}},onComponentHide:function(){var a=this;if(a.rendered&&a.isVisible()){a.hide();a.showNext=true}},onComponentShow:function(){if(this.showNext){this.show()}delete this.showNext},sizeMask:function(){var b=this,c=b.activeOwner||b.target,a=b.external?b.getOwner().el:b.getMaskTarget();if(b.rendered&&b.isVisible()){if(b.external){if(!b.isElement&&c.floating){b.onOwnerToFront(c,c.el.getZIndex())}b.el.setSize(a.getSize()).alignTo(a,"tl-tl")}b.msgWrapEl.center(b.el)}},bindStore:function(a,b){var c=this;Ext.destroy(c.proxyListeners);c.mixins.storeholder.bindStore.apply(c,arguments);a=c.store;if(a){while(a.getSource){a=a.getSource()}if(!a.loadsSynchronously()){c.proxyListeners=a.getProxy().on({exception:c.onLoad,scope:c,destroyable:true})}if(a.isLoading()){c.onBeforeLoad()}}},getStoreListeners:function(b){var d=this.onLoad,c=this.onBeforeLoad,a={cachemiss:c,cachefilled:{fn:d,buffer:100}};if(!b.loadsSynchronously()){a.beforeload=c;a.load=d}return a},onDisable:function(){Ext.Component.prototype.onDisable.apply(this,arguments);if(this.loading){this.onLoad()}},getOwner:function(){return this.ownerCt||this.ownerCmp||this.floatParent},getMaskTarget:function(){var a=this.getOwner();if(this.isElement){return this.target}return this.useTargetEl?a.getTargetEl():(a.getMaskTarget()||Ext.getBody())},onBeforeLoad:function(){var c=this,a=c.getOwner(),b;if(!c.disabled){c.loading=true;if(a.componentLayoutCounter){c.maybeShow()}else{b=a.afterComponentLayout;a.afterComponentLayout=function(){a.afterComponentLayout=b;b.apply(a,arguments);c.maybeShow()}}}},maybeShow:function(){var b=this,a=b.getOwner();if(!a.isVisible(true)){b.showNext=true}else{if(b.loading&&a.rendered){b.show()}}},hide:function(){var b=this,a=b.ownerCt;if(b.isElement){a.unmask();b.fireEvent("hide",this);return}if(a){a.enableTabbing();a.setMasked(false)}delete b.showNext;return Ext.Component.prototype.hide.apply(this,arguments)},show:function(){var a=this;if(a.isElement){a.ownerCt.mask(this.useMsg?this.msg:"",this.msgCls);a.fireEvent("show",this);return}return Ext.Component.prototype.show.apply(this,arguments)},afterShow:function(){var b=this,a=b.ownerCt;b.loading=true;Ext.Component.prototype.afterShow.apply(this,arguments);a.disableTabbing();a.setMasked(true);b.el.restoreTabbableState();b.syncMaskState()},syncMaskState:function(){var c=this,b=c.ownerCt,a=c.el;if(c.isVisible()){if(c.hasOwnProperty("msgWrapCls")){a.dom.className=c.msgWrapCls}if(c.useMsg){c.msgTextEl.setHtml(c.msg)}else{c.msgEl.hide()}if(c.shim||Ext.useShims){a.enableShim(null,true)}else{a.disableShim()}if(b.el.contains(Ext.Element.getActiveElement())){c.focus()}c.sizeMask()}},onLoad:function(){this.loading=false;this.hide()},beforeDestroy:function(){this.ownerCt=null;this.bindStore(null);Ext.Component.prototype.beforeDestroy.call(this)},onDestroy:function(){var a=this;if(a.isElement){a.ownerCt.unmask()}Ext.Component.prototype.onDestroy.call(this)}},1,["loadmask"],["component","box","loadmask"],{component:true,box:true,loadmask:true},["widget.loadmask"],[[Ext.util.StoreHolder.prototype.mixinId||Ext.util.StoreHolder.$className,Ext.util.StoreHolder]],[Ext,"LoadMask"],0));(Ext.cmd.derive("Ext.layout.component.Component",Ext.layout.Layout,{type:"component",isComponentLayout:true,nullBox:{},usesContentHeight:true,usesContentWidth:true,usesHeight:true,usesWidth:true,widthCache:{},heightCache:{},beginLayoutCycle:function(d,q){var l=this,c=l.owner,h=d.ownerCtContext,i=d.heightModel,j=d.widthModel,k=c.el.dom===document.body,g=c.lastBox||l.nullBox,n=c.el.lastBox||l.nullBox,a=!k,e=d.isTopLevel,m,o,b,p;Ext.layout.Layout.prototype.beginLayoutCycle.call(this,d,q);if(q){if(l.usesContentWidth){++d.consumersContentWidth}if(l.usesContentHeight){++d.consumersContentHeight}if(l.usesWidth){++d.consumersWidth}if(l.usesHeight){++d.consumersHeight}if(h&&!h.hasRawContent){m=c.ownerLayout;if(m){if(m.usesWidth){++d.consumersWidth}if(m.usesHeight){++d.consumersHeight}}}}if(j.configured){b=c[j.names.width];if(e&&j.calculatedFrom){b=g.width}if(!k){a=l.setWidthInDom||(q?b!==n.width:j.constrained)}d.setWidth(b,a)}else{if(e){if(j.calculated){o=g.width;d.setWidth(o,o!==n.width)}o=g.x;d.setProp("x",o,o!==n.x)}}if(i.configured){p=c[i.names.height];if(e&&i.calculatedFrom){p=g.height}if(!k){a=q?p!==n.height:i.constrained}d.setHeight(p,a)}else{if(e){if(i.calculated){o=g.height;d.setHeight(o,o!==n.height)}o=g.y;d.setProp("y",o,o!==n.y)}}},finishedLayout:function(b){var h=this,k=b.children,a=h.owner,e,c,j,d,g;if(k){e=k.length;for(c=0;c','
    {text}
    ',"
    ",'"],componentLayout:"progressbar",ariaRole:"progressbar",initRenderData:function(){var a=this,b=a.value||0;return Ext.apply(Ext.Component.prototype.initRenderData.call(this),{internalText:!a.hasOwnProperty("textEl"),text:a.text||" ",percentage:b*100})},onRender:function(){var a=this;Ext.Component.prototype.onRender.apply(this,arguments);if(a.textEl){a.textEl=Ext.get(a.textEl);a.updateText(a.text)}else{a.textEl=a.el.select("."+a.baseCls+"-text")}},updateValue:function(a){this.updateProgress(a,Math.round(a*100)+"%")},updateProgress:function(e,g,a){e=e||0;var d=this,b=d.value,c=d.getTextTpl();d.value=e||(e=0);if(g!=null){d.updateText(g)}else{if(c){d.updateText(c.apply({value:e,percent:e*100}))}}if(d.rendered&&!d.destroyed){if(a===true||(a!==false&&d.animate)){d.bar.stopAnimation();d.bar.animate(Ext.apply({from:{width:(b*100)+"%"},to:{width:(e*100)+"%"}},d.animate))}else{d.bar.setStyle("width",(e*100)+"%")}}d.fireEvent("update",d,e,g);return d},updateText:function(b){var a=this;a.text=b;if(a.rendered){a.textEl.setHtml(a.text)}return a},applyText:function(a){this.updateText(a)},getText:function(){return this.text},wait:function(c){var b=this,a;if(!b.waitTimer){a=b;c=c||{};b.updateText(c.text);b.waitTimer=Ext.TaskManager.start({run:function(d){var e=c.increment||10;d-=1;b.updateProgress(((((d+e)%e)+1)*(100/e))*0.01,null,c.animate)},interval:c.interval||1000,duration:c.duration,onStop:function(){if(c.fn){c.fn.apply(c.scope||b)}b.reset()},scope:a})}return b},isWaiting:function(){return this.waitTimer!==null},reset:function(a){var b=this;b.updateProgress(0);b.clearTimer();if(a===true){b.hide()}return b},clearTimer:function(){var a=this;if(a.waitTimer){a.waitTimer.onStop=null;Ext.TaskManager.stop(a.waitTimer);a.waitTimer=null}},onDestroy:function(){var b=this,a=b.bar;b.clearTimer();if(b.rendered){if(b.textEl.isComposite){b.textEl.clear()}Ext.destroyMembers(b,"textEl","progressBar");if(a&&b.animate){a.stopAnimation()}}Ext.Component.prototype.onDestroy.call(this)}},0,["progressbar"],["component","box","progressbar"],{component:true,box:true,progressbar:true},["widget.progressbar"],[[Ext.ProgressBase.prototype.mixinId||Ext.ProgressBase.$className,Ext.ProgressBase]],[Ext,"ProgressBar"],0));(Ext.cmd.derive("Ext.dom.ButtonElement",Ext.dom.Element,{setSize:function(d,a,b){var e=this,c=e.component;Ext.dom.Element.prototype.setSize.call(this,d,a,b);c.btnWrap.setStyle("table-layout",(!d||d==="auto")?"":"fixed");c.btnEl.setStyle("height",(!a||a==="auto")?"":"auto");return e},setStyle:function(g,e){var d=this,b=d.component,c,a;Ext.dom.Element.prototype.setStyle.call(this,g,e);if(g){if(g==="width"||(typeof g!=="string"&&"width" in g)){c=e||g.width;b.btnWrap.setStyle("table-layout",(!c||c==="auto")?"":"fixed")}if(g==="height"||(typeof g!=="string"&&"height" in g)){a=e||g.height;b.btnEl.setStyle("height",(!a||a==="auto")?"":"auto")}}return d},setHeight:function(a,b){Ext.dom.Element.prototype.setHeight.call(this,a,b);this.component.btnEl.setStyle("height",(!a||a==="auto")?"":"auto");return this},setWidth:function(b,a){Ext.dom.Element.prototype.setWidth.call(this,b,a);this.component.btnWrap.setStyle("table-layout",(!b||b==="auto")?"":"fixed");return this}},0,0,0,0,0,0,[Ext.dom,"ButtonElement"],0));(Ext.cmd.derive("Ext.button.Manager",Ext.Base,{singleton:true,alternateClassName:"Ext.ButtonToggleManager",groups:{},pressedButton:null,init:function(){var a=this;if(!a.initialized){Ext.getDoc().on({mouseup:a.onDocumentMouseUp,scope:a});a.initialized=true}},onButtonMousedown:function(a,c){var b=this.pressedButton;if(b){b.onMouseUp(c)}this.pressedButton=a},onDocumentMouseUp:function(b){var a=this.pressedButton;if(a){a.onMouseUp(b);this.pressedButton=null}},toggleGroup:function(b,e){if(e){var d=this.groups[b.toggleGroup],c=d.length,a;for(a=0;a{[values.$comp.renderIcon(values)]}{text}{[values.$comp.renderIcon(values)]}{[values.$comp.getAfterMarkup ? values.$comp.getAfterMarkup(values) : ""]} {closeText} tabindex="{tabIndex}" {$}="{.}">{arrowElText}
    ',iconTpl:'background-image:url({iconUrl});
    font-family:{glyphFontFamily};">&#{glyph};',scale:"small",allowedScales:["small","medium","large"],arrowAlign:"right",arrowCls:"arrow",maskOnDisable:false,shrinkWrap:3,frame:true,autoEl:{tag:"a",hidefocus:"on",unselectable:"on"},hasFrameTable:function(){return this.href&&this.frameTable},frameTableListener:function(){if(!this.disabled){this.doNavigate()}},doNavigate:function(){if(this.hrefTarget==="_blank"){window.open(this.getHref(),this.hrefTarget)}else{location.href=this.getHref()}},_triggerRegion:{},initComponent:function(){var a=this;a.addCls("x-unselectable");Ext.Component.prototype.initComponent.call(this);if(a.menu){a.split=true;a.setMenu(a.menu,false,true)}if(a.url){a.href=a.url}a.configuredWithPreventDefault=a.hasOwnProperty("preventDefault");if(a.href&&!a.configuredWithPreventDefault){a.preventDefault=false}if(Ext.isString(a.toggleGroup)&&a.toggleGroup!==""){a.enableToggle=true}if(a.html&&!a.text){a.text=a.html;delete a.html}},getElConfig:function(){var c=this,b=Ext.Component.prototype.getElConfig.call(this),a=c.getHref(),d=c.hrefTarget;if(b.tag==="a"){if(!c.disabled){b.tabIndex=c.tabIndex}if(a){if(!c.disabled){b.href=a;if(d){b.target=d}}}}if(!c.ariaStaticRoles[c.ariaRole]){if(c.menu&&!c.isSplitButton){b["aria-haspopup"]=true}if(c.enableToggle){b["aria-pressed"]=!!c.pressed}}return b},beforeRender:function(){Ext.Component.prototype.beforeRender.call(this);if(this.pressed){this.addCls(this._pressedCls)}},initRenderData:function(){return Ext.apply(Ext.Component.prototype.initRenderData.call(this),this.getTemplateArgs())},getMenu:function(){return this.menu||null},setMenu:function(i,h,e){var g=this,b=g.menu,a=g.isSplitButton?g.arrowEl&&g.arrowEl.dom:g.ariaEl.dom,c,d;if(b&&!e){if(h!==false&&g.destroyMenu){b.destroy()}b.ownerCmp=null}if(i){c=i.isMenu;i=Ext.menu.Manager.get(i,{ownerCmp:g});i.setOwnerCmp(g,c);i.menuClickBuffer=250;g.mon(i,{scope:g,show:g.onMenuShow,hide:g.onMenuHide});if(!b&&g.getArrowVisible()){g.split=true;if(g.rendered){g._addSplitCls();g.updateLayout()}}g.menu=i;if(a){a.setAttribute("aria-haspopup",true);a.setAttribute("aria-owns",i.id)}else{d=g.isSplitButton?(g.ariaArrowElAttributes||(g.ariaArrowElAttributes={})):(g.ariaRenderAttributes||(g.ariaRenderAttributes={}));d["aria-haspopup"]=true;d["aria-owns"]=i.id}}else{if(g.rendered){a.removeAttribute("aria-haspopup");a.removeAttribute("aria-owns");g._removeSplitCls();g.updateLayout()}else{d=g.isSplitButton?g.ariaArrowElAttributes:g.ariaRenderAttributes;if(d){delete d["aria-haspopup"];delete d["aria-owns"]}}g.split=false;g.menu=null}},onRender:function(){var c=this,d,a,b;Ext.Component.prototype.onRender.apply(this,arguments);a=c.el;if(c.tooltip){c.setTooltip(c.tooltip,true)}if(c.handleMouseEvents){b={scope:c,mouseover:c.onMouseOver,mouseout:c.onMouseOut,mousedown:c.onMouseDown};if(c.split){b.mousemove=c.onMouseMove}}else{b={scope:c}}if(Ext.supports.Touch){b.touchstart=c.onTouchStart}if(c.repeat){c.mon(new Ext.util.ClickRepeater(a,Ext.isObject(c.repeat)?c.repeat:{}),"click",c.onRepeatClick,c)}else{if(b[c.clickEvent]){d=true}else{b[c.clickEvent]=c.onClick}}c.mon(a,b);if(c.hasFrameTable()){c.mon(c.frameTable,"click",c.frameTableListener,c)}if(d){c.mon(a,c.clickEvent,c.onClick,c)}Ext.button.Manager.register(c)},onFocusLeave:function(a){Ext.Component.prototype.onFocusLeave.call(this,a);if(this.menu){this.menu.hide()}},getTemplateArgs:function(){var i=this,c=i._btnCls,e=i._baseIconCls,a=i.getIconAlign(),j=i.glyph,h=Ext._glyphFontFamily,k=i.text,d=i._hasIcon(),g=i._hasIconCls,b;if(typeof j==="string"){b=j.split("@");j=b[0];h=b[1]}return{split:i.isSplitButton,innerCls:i._innerCls,splitCls:i.getArrowVisible()?i.getSplitCls():"",iconUrl:i.icon,iconCls:i.iconCls,glyph:j,glyphCls:j?i._glyphCls:"",glyphFontFamily:h,text:k||" ",closeText:i.closeText,textCls:k?i._textCls:"",noTextCls:k?"":i._noTextCls,hasIconCls:d?g:"",btnWrapCls:i._btnWrapCls,btnWrapStyle:i.width?"table-layout:fixed;":"",btnElStyle:i.height?"height:auto;":"",btnCls:c,baseIconCls:e,iconBeforeText:a==="left"||a==="top",iconAlignCls:d?(g+"-"+a):"",textAlignCls:c+"-"+i.getTextAlign(),arrowElCls:i._arrowElCls,tabIndex:i.tabIndex}},renderIcon:function(a){return this.getTpl("iconTpl").apply(a)},setHref:function(a){var b=this,d=b.hrefTarget,c;b.href=a;if(!b.configuredWithPreventDefault){b.preventDefault=!a}if(b.rendered){c=b.el.dom;if(!a||b.disabled){c.removeAttribute("href");c.removeAttribute("hrefTarget")}else{c.href=b.getHref();if(d){c.target=d}}}},getHref:function(){var b=this,a=b.href;return a?Ext.urlAppend(a,Ext.Object.toQueryString(Ext.apply({},b.params,b.baseParams))):false},setParams:function(c){var a=this,b;a.params=c;if(a.rendered){b=a.el.dom;if(a.disabled){b.removeAttribute("href")}else{b.href=a.getHref()||""}}},getSplitCls:function(){var a=this;return a.split?(a.baseCls+"-"+a.arrowCls)+" "+(a.baseCls+"-"+a.arrowCls+"-"+a.arrowAlign):""},setIcon:function(b){b=b||"";var c=this,a=c.btnIconEl,d=c.icon||"";c.icon=b;if(b!==d){if(a){a.setStyle("background-image",b?"url("+b+")":"");c._syncHasIconCls();if(c.didIconStateChange(d,b)){c.updateLayout()}}c.fireEvent("iconchange",c,d,b)}return c},setIconCls:function(b){b=b||"";var d=this,a=d.btnIconEl,c=d.iconCls||"";d.iconCls=b;if(c!==b){if(a){a.removeCls(c);a.addCls(b);d._syncHasIconCls();if(d.didIconStateChange(c,b)){d.updateLayout()}}d.fireEvent("iconchange",d,c,b)}return d},setGlyph:function(g){g=g||0;var e=this,b=e.btnIconEl,c=e.glyph,h=e._glyphCls,a,d;e.glyph=g;if(b){if(typeof g==="string"){d=g.split("@");g=d[0];a=d[1]||Ext._glyphFontFamily}if(!g){b.dom.innerHTML="";b.removeCls(h)}else{if(c!==g){b.dom.innerHTML="&#"+g+";";b.addCls(h)}}if(a){b.setStyle("font-family",a)}e._syncHasIconCls();if(e.didIconStateChange(c,g)){e.updateLayout()}}e.fireEvent("glyphchange",e,e.glyph,c);return e},setTooltip:function(c,a){var b=this;if(b.rendered){if(!a||!c){b.clearTip()}if(c){if(Ext.quickTipsActive&&Ext.isObject(c)){Ext.tip.QuickTipManager.register(Ext.apply({target:b.el.id},c));b.tooltip=c}else{b.el.dom.setAttribute(b.getTipAttr(),c)}}}else{b.tooltip=c}return b},updateIconAlign:function(g,d){var c=this,b,a,e;if(c.rendered){b=c.btnEl;a=c.btnIconEl;e=c._hasIconCls;if(d){b.removeCls(e+"-"+d)}b.addCls(e+"-"+g);if(g==="top"||g==="left"){b.insertFirst(a)}else{b.appendChild(a)}c.updateLayout()}},updateTextAlign:function(e,d){var c=this,b=c.btnEl,a=c._btnCls;if(c.rendered){b.removeCls(a+"-"+d);b.addCls(a+"-"+e)}},getTipAttr:function(){return this.tooltipType==="qtip"?"data-qtip":"title"},getRefItems:function(a){var c=this.menu,b;if(c){b=c.getRefItems(a);b.unshift(c)}return b||[]},clearTip:function(){var b=this,a=b.el;if(Ext.quickTipsActive&&Ext.isObject(b.tooltip)){Ext.tip.QuickTipManager.unregister(a)}else{a.dom.removeAttribute(b.getTipAttr())}},beforeDestroy:function(){var a=this;if(a.rendered){a.clearTip()}Ext.destroy(a.repeater);Ext.Component.prototype.beforeDestroy.call(this)},onDestroy:function(){var a=this,b=a.menu;if(a.rendered){Ext.destroy(a.keyMap);delete a.keyMap}if(b&&a.destroyMenu){a.menu=Ext.destroy(b)}Ext.button.Manager.unregister(a);Ext.Component.prototype.onDestroy.call(this)},setHandler:function(b,a){this.handler=b;if(arguments.length>1){this.scope=a}return this},updateText:function(d,a){d=d==null?"":String(d);a=a||"";var c=this,e=c.btnInnerEl,b=c.btnEl;if(c.rendered){e.setHtml(d||" ");b[d?"addCls":"removeCls"](c._textCls);b[d?"removeCls":"addCls"](c._noTextCls);c.updateLayout()}c.fireEvent("textchange",c,a,d)},didIconStateChange:function(a,c){var b=Ext.isEmpty(c);return Ext.isEmpty(a)?!b:b},click:function(a){return this.onClick(a)},setPressed:function(a){return this.toggle(a!==false)},toggle:function(d,b){var c=this,a=c.ariaEl.dom;d=d===undefined?!c.pressed:!!d;if(c.fireEvent("beforetoggle",c,d)!==false){if(d!==c.pressed){c[d?"addCls":"removeCls"](c._pressedCls);c.pressed=d;if(a){a.setAttribute("aria-pressed",d)}if(!b){c.fireEvent("toggle",c,d);Ext.callback(c.toggleHandler,c.scope,[c,d],0,c);if(c.reference&&c.publishState){c.publishState("pressed",d)}}}}return c},maybeShowMenu:function(a){if(this.menu){this.showMenu(a)}},showMenu:function(a){var c=this,d=c.menu,b=!a||a.pointerType;if(d&&c.rendered){if(c.tooltip&&Ext.quickTipsActive&&c.getTipAttr()!=="title"){Ext.tip.QuickTipManager.getQuickTip().cancelShow(c.el)}if(d.isVisible()){if(b){d.hide()}else{d.focus()}}else{if(!a||c.showEmptyMenu||d.items.getCount()>0){d.autoFocus=!b;d.showBy(c.el,c.menuAlign)}}}return c},hideMenu:function(){if(this.hasVisibleMenu()){this.menu.hide()}return this},hasVisibleMenu:function(){var a=this.menu;return a&&a.rendered&&a.isVisible()},onRepeatClick:function(a,b){this.onClick(b)},onTouchStart:function(a){this.doPreventDefault(a)},onEnterKey:function(a){this.onClick(a);a.stopEvent();return false},onClick:function(b){var a=this;a.doPreventDefault(b);if(b.type!=="keydown"&&b.button){return}if(!a.disabled){a.doToggle();a.maybeShowMenu(b);a.fireHandler(b)}},doPreventDefault:function(a){if(a&&(this.preventDefault||(this.disabled&&this.getHref()))){a.preventDefault()}},fireHandler:function(b){var a=this;if(a.fireEvent("click",a,b)!==false&&!a.destroyed){Ext.callback(a.handler,a.scope,[a,b],0,a)}},doToggle:function(){var a=this;if(a.enableToggle&&(a.allowDepress!==false||!a.pressed)){a.toggle()}},onMouseOver:function(b){var a=this;if(!a.disabled&&!b.within(a.el,true,true)){a.onMouseEnter(b)}},onMouseOut:function(b){var a=this;if(!b.within(a.el,true,true)){if(a.overMenuTrigger){a.onMenuTriggerOut(b)}a.onMouseLeave(b)}},onMouseMove:function(c){var a=this,b=a.overMenuTrigger;if(a.split){if(a.isWithinTrigger(c)){if(!b){a.onMenuTriggerOver(c)}}else{if(b){a.onMenuTriggerOut(c)}}}},isWithinTrigger:function(d){var c=this,b=c.el,g,a;g=(c.arrowAlign==="right")?d.getX()-c.getX():d.getY()-b.getY();a=c.getTriggerRegion();return g>a.begin&&g=0;c--){e=h[c];if(j.forceSelection&&!c&&!b){e.pressed=true}if(e.pressed){b=true;d=e.value;if(d==null){d=j.items.indexOf(e)}if(!Ext.Array.contains(l,d)){l.unshift(d)}}}}g=l.length;for(c=0;c-1){g=d.slice(g);g.splice(i,1)}}else{if(g===a){g=null}}}e.setValue(g);e.fireEvent("toggle",e,c,h)},_syncItemClasses:function(a){var h=this,b,d,k,g,e,l,j,c;if(!a&&!h.rendered){return}b=h._getFirstCls();d=h._middleCls;k=h._getLastCls();g=h.items.items;e=g.length;l=[];for(c=0;c1){l[0].addCls(b);for(c=1;c{iconMarkup}
    role="{textElRole}">{text}
    {iconMarkup}',iconTpl:'',_textAlignClasses:{left:"x-title-align-left",center:"x-title-align-center",right:"x-title-align-right"},_iconAlignClasses:{top:"x-title-icon-top",right:"x-title-icon-right",bottom:"x-title-icon-bottom",left:"x-title-icon-left"},_rotationClasses:{0:"x-title-rotate-none",1:"x-title-rotate-right",2:"x-title-rotate-left"},_rotationAngles:{1:90,2:270},baseCls:"x-title",_titleSuffix:"-title",_glyphCls:"x-title-glyph",_iconWrapCls:"x-title-icon-wrap",_baseIconCls:"x-title-icon",_itemCls:"x-title-item",_textCls:"x-title-text",afterComponentLayout:function(){var d=this,b=d.getRotation(),a,e,c;if(b&&!Ext.isIE8){c=d.el;a=d.lastBox;e=a.x;c.setStyle(d._getVerticalAdjustDirection(),(e+((b===1)?a.width:-a.height))+"px")}Ext.Component.prototype.afterComponentLayout.call(this)},onRender:function(){var c=this,a=c.getRotation(),b=c.el;Ext.Component.prototype.onRender.call(this);if(a){b.setVertical(c._rotationAngles[a])}if(Ext.supports.FixedTableWidthBug){b._needsTableWidthFix=true}},applyText:function(a){if(!a){a=" "}return a},beforeRender:function(){var a=this;Ext.Component.prototype.beforeRender.call(this);a.addCls(a._rotationClasses[a.getRotation()]);a.addCls(a._textAlignClasses[a.getTextAlign()])},getIconMarkup:function(){return this.getTpl("iconTpl").apply(this.getIconRenderData())},getIconRenderData:function(){var g=this,c=g.getIcon(),b=g.getIconCls(),e=g.getGlyph(),h=Ext._glyphFontFamily,a=g.getIconAlign(),d;if(typeof e==="string"){d=e.split("@");e=d[0];h=d[1]}return{id:g.id,ui:g.ui,itemCls:g._itemCls,iconUrl:c,iconCls:b,iconWrapCls:g._iconWrapCls,baseIconCls:g._baseIconCls,iconAlignCls:g._iconAlignClasses[a],glyph:e,glyphCls:e?g._glyphCls:"",glyphFontFamily:h}},initRenderData:function(){var b=this,a,c;c=Ext.apply({text:b.getText(),textElRole:b.textElRole,id:b.id,ui:b.ui,itemCls:b._itemCls,textCls:b._textCls,iconMarkup:null,iconBeforeTitle:null},Ext.Component.prototype.initRenderData.call(this));if(b._hasIcon()){a=b.getIconAlign();c.iconMarkup=b.getIconMarkup();c.iconBeforeTitle=(a==="top"||a==="left")}return c},onAdded:function(b,g,a){var d=this,e=d._titleSuffix,c=b.baseCls;d.addCls([c+e,c+e+"-"+b.ui]);Ext.Component.prototype.onAdded.call(this,b,g,a)},updateGlyph:function(g,c){g=g||0;var e=this,h=e._glyphCls,b,a,d;e.glyph=g;if(e.rendered){e._syncIconVisibility();b=e.iconEl;if(typeof g==="string"){d=g.split("@");g=d[0];a=d[1]||Ext._glyphFontFamily}if(!g){b.dom.innerHTML="";b.removeCls(h)}else{if(c!==g){b.dom.innerHTML="&#"+g+";";b.addCls(h)}}if(a){b.setStyle("font-family",a)}if(e._didIconStateChange(c,g)){e.updateLayout()}}},updateIcon:function(b,d){b=b||"";var c=this,a;if(c.rendered&&b!==d){c._syncIconVisibility();a=c.iconEl;a.setStyle("background-image",b?"url("+b+")":"");if(c._didIconStateChange(d,b)){c.updateLayout()}}},updateIconAlign:function(g,c){var b=this,e=b.iconWrapEl,a,d;if(b.iconWrapEl){a=b.el;d=b._iconAlignClasses;if(c){e.removeCls(d[c])}e.addCls(d[g]);if(g==="top"||g==="left"){a.insertFirst(e)}else{a.appendChild(e)}b.updateLayout()}},updateIconCls:function(b,c){b=b||"";var d=this,a;if(d.rendered&&c!==b){d._syncIconVisibility();a=d.iconEl;if(c){a.removeCls(c)}a.addCls(b);if(d._didIconStateChange(c,b)){d.updateLayout()}}},updateRotation:function(b,a){var d=this,c,e;if(d.rendered){c=d.el;e=d._rotationClasses;d.removeCls(e[a]);d.addCls(e[b]);c.setHorizontal();if(b){c.setVertical(d._rotationAngles[b])}c.setStyle({right:"",left:"",top:"",height:"",width:""});d.lastBox=null;d.updateLayout()}},updateText:function(a){if(this.rendered){this.textEl.setHtml(a);this.updateLayout()}},updateTextAlign:function(d,b){var a=this,c=a._textAlignClasses;if(a.rendered){if(b){a.removeCls(c[b])}a.addCls(c[d]);a.updateLayout()}},privates:{_getVerticalAdjustDirection:function(){return"left"},_didIconStateChange:function(a,c){var b=Ext.isEmpty(c);return Ext.isEmpty(a)?!b:b},_hasIcon:function(){return !!(this.getIcon()||this.getIconCls()||this.getGlyph())},_syncIconVisibility:function(){var e=this,d=e.el,a=e._hasIcon(),g=e.iconWrapEl,c,b;if(a&&!g){b=e.iconAlign;c=(b==="left"||b==="top");d.dom.insertAdjacentHTML(c?"afterbegin":"beforeend",e.getIconMarkup());g=e.iconWrapEl=d[c?"first":"last"]();e.iconEl=g.first()}if(g){g.setDisplayed(a)}}}},0,["title"],["component","box","title"],{component:true,box:true,title:true},["widget.title"],0,[Ext.panel,"Title"],0));(Ext.cmd.derive("Ext.panel.Tool",Ext.Component,{isTool:true,baseCls:"x-tool",disabledCls:"x-tool-disabled",toolPressedCls:"x-tool-pressed",toolOverCls:"x-tool-over",childEls:["toolEl"],renderTpl:[''],toolOwner:null,tooltipType:"qtip",stopEvent:true,ariaRole:"button",focusable:true,tabIndex:0,keyHandlers:{SPACE:"onClick",ENTER:"onClick"},cacheHeight:true,cacheWidth:true,initComponent:function(){var a=this;a.type=a.type||a.id;Ext.applyIf(a.renderData,{baseCls:a.baseCls,type:a.type});a.tooltip=a.tooltip||a.qtip;Ext.Component.prototype.initComponent.call(this)},afterRender:function(){var a=this,b;Ext.Component.prototype.afterRender.apply(this,arguments);a.el.on({click:a.onClick,mousedown:a.onMouseDown,mouseover:a.onMouseOver,mouseout:a.onMouseOut,scope:a});b=a.tooltip;if(b){a.setTooltip(b)}},tipAttrs:{qtip:"data-qtip"},setTooltip:function(h,d){var e=this,b=e.tooltip,g=e.tooltipType,i=e.id,c=e.el,a;if(b&&Ext.quickTipsActive&&Ext.isObject(b)){Ext.tip.QuickTipManager.unregister(i)}e.tooltip=h;if(d){e.tooltipType=d}if(h){if(Ext.quickTipsActive&&Ext.isObject(h)){Ext.tip.QuickTipManager.register(Ext.apply({target:i},h))}else{if(c){if(d&&g&&d!==g){a=e.tipAttrs[g]||"title";c.dom.removeAttribute(a)}a=e.tipAttrs[d||g]||"title";c.dom.setAttribute(a,h)}}if(a!=="title"&&e.ariaRole&&e.ariaRole!=="presentation"){if(c){c.dom.setAttribute("aria-label",h)}else{e.ariaRenderAttributes=e.ariaRenderAttributes||{};e.ariaRenderAttributes["aria-label"]=h}}}},setType:function(a){var b=this,c=b.type;b.type=a;if(b.rendered){if(c){b.toolEl.removeCls(b.baseCls+"-"+c)}b.toolEl.addCls(b.baseCls+"-"+a)}else{b.renderData.type=a}return b},onDestroy:function(){var a=this,b=a.keyMap;a.setTooltip(null);delete a.toolOwner;Ext.Component.prototype.onDestroy.call(this)},privates:{onClick:function(c,b){var a=this;if(a.disabled){return false}if(c.type!=="keydown"){a.el.removeCls(a.toolPressedCls+" "+a.toolOverCls)}if(a.stopEvent!==false){c.stopEvent()}if(a.handler){Ext.callback(a.handler,a.scope,[c,b,a.ownerCt,a],0,a)}else{if(a.callback){Ext.callback(a.callback,a.scope,[a.toolOwner||a.ownerCt,a,c],0,a)}}a.fireEvent("click",a,c,a.toolOwner||a.ownerCt);return true},onMouseDown:function(a){a.preventDefault();if(this.disabled){return false}this.el.addCls(this.toolPressedCls)},onMouseOver:function(){if(this.disabled){return false}this.el.addCls(this.toolOverCls)},onMouseOut:function(){this.el.removeCls(this.toolOverCls)}}},0,["tool"],["component","box","tool"],{component:true,box:true,tool:true},["widget.tool"],0,[Ext.panel,"Tool"],0));(Ext.cmd.derive("Ext.util.KeyMap",Ext.Base,{alternateClassName:"Ext.KeyMap",eventName:"keydown",constructor:function(a){var b=this;if((arguments.length!==1)||(typeof a==="string")||a.dom||a.tagName||a===document||a.isComponent){b.legacyConstructor.apply(b,arguments);return}Ext.apply(b,a);b.bindings=[];if(!b.target.isComponent){b.target=Ext.get(b.target)}if(b.binding){b.addBinding(b.binding)}else{if(a.key){b.addBinding(a)}}b.enable()},legacyConstructor:function(b,d,a){var c=this;Ext.apply(c,{target:Ext.get(b),eventName:a||c.eventName,bindings:[]});if(d){c.addBinding(d)}c.enable()},addBinding:function(e){var c=this,d=e.key,b,a;if(c.processing){c.bindings=c.bindings.slice(0)}if(Ext.isArray(e)){for(b=0,a=e.length;b=j){return h}}}if(!c){return}h=g.findNextFocusableChild({beforeRender:k,items:e,step:true});if(h){g.activateFocusable(h)}return h},clearFocusables:function(){var e=this,b=e.getFocusables(),a=b.length,d,c;for(c=0;c0?(g0?g+b:d-1);for(;;c+=b){if(g<0&&(c>=d||c<0)){return null}else{if(c>=d){c=-1;continue}else{if(c<0){c=d;continue}else{if(c===g){return null}}}}h=e[c];if(!h||!h.focusable||h.disabled){continue}if(j||(h.isFocusable&&h.isFocusable())){return h}}return null},getFocusableContainerEl:function(){return this.el},onFocusableChildAdd:function(a){if(this.enableFocusableContainer){return this.doFocusableChildAdd(a)}},activateFocusableContainerEl:function(a){a=a||this.getFocusableContainerEl();if(a){a.set({tabIndex:this.activeChildTabIndex})}},deactivateFocusableContainerEl:function(a){a=a||this.getFocusableContainerEl();if(a){a.set({tabIndex:undefined})}},isFocusableContainerActive:function(){var d=this,c=false,b,e,a;b=d.getFocusableContainerEl();if(b&&b.isTabbable&&b.isTabbable()){c=true}else{e=d.lastFocusedChild;a=e&&e.getFocusEl&&e.getFocusEl();if(a&&a.isTabbable&&a.isTabbable()){c=true}}return c},doFocusableChildAdd:function(a){if(a.focusable){a.focusableContainer=this}},onFocusableChildRemove:function(a){if(this.enableFocusableContainer){return this.doFocusableChildRemove(a)}a.focusableContainer=null},doFocusableChildRemove:function(a){if(a===this.lastFocusedChild){this.lastFocusedChild=null;this.activateFocusableContainerEl()}},onFocusableContainerMousedown:function(c,b){var a=Ext.Component.fromElement(b);this.mousedownTimestamp=a===this?Ext.Date.now():0;if(a===this){c.preventDefault()}},onFocusEnter:function(c){var a=this,b=c.toComponent,d=a.mousedownTimestamp,h=50,g;if(!a.enableFocusableContainer){return null}a.mousedownTimestamp=0;if(b===a){if(!d||Ext.Date.now()-d>h){g=a.initDefaultFocusable();if(g){a.deactivateFocusableContainerEl();g.focus()}}}else{a.deactivateFocusableContainerEl()}return b},onFocusLeave:function(c){var b=this,a=b.lastFocusedChild;if(!b.enableFocusableContainer){return}if(!b.destroyed&&!b.destroying){b.clearFocusables();if(a&&!a.disabled){b.activateFocusable(a)}else{b.activateFocusableContainerEl()}}},beforeFocusableChildBlur:Ext.privateFn,afterFocusableChildBlur:Ext.privateFn,beforeFocusableChildFocus:function(b){var a=this;if(!a.enableFocusableContainer){return}a.clearFocusables();a.activateFocusable(b);if(b.needArrowKeys){a.guardFocusableChild(b)}},guardFocusableChild:function(d){var c=this,a=c.activeChildTabIndex,b;b=c.findNextFocusableChild({child:d,step:-1});if(b){b.setTabIndex(a)}b=c.findNextFocusableChild({child:d,step:1});if(b){b.setTabIndex(a)}},afterFocusableChildFocus:function(a){if(!this.enableFocusableContainer){return}this.lastFocusedChild=a},beforeFocusableChildEnable:Ext.privateFn,onFocusableChildEnable:function(b){var a=this;if(!a.enableFocusableContainer){return}if(b!==a.lastFocusedChild){a.deactivateFocusable(b);if(!a.isFocusableContainerActive()){a.activateFocusableContainerEl()}}},beforeFocusableChildDisable:function(c){var b=this,a;if(!b.enableFocusableContainer||b.destroying||b.destroyed){return}if(c.hasFocus){a=b.findNextFocusableChild({child:c})||c.findFocusTarget();if(a){a.focus()}}},onFocusableChildDisable:function(d){var c=this,a=c.lastFocusedChild,b;if(!c.enableFocusableContainer||c.destroying||c.destroyed){return}if(d===a){c.activateFocusableContainerEl()}b=c.findNextFocusableChild({step:1});if(!b){c.deactivateFocusableContainerEl()}},onFocusableChildShow:Ext.privateFn,onFocusableChildHide:Ext.privateFn,onFocusableChildMasked:Ext.privateFn,onFocusableChildDestroy:Ext.privateFn,onFocusableChildUpdate:Ext.privateFn}},0,0,0,0,0,0,[Ext.util,"FocusableContainer"],0));(Ext.cmd.derive("Ext.panel.Header",Ext.panel.Bar,{isHeader:true,defaultType:"tool",indicateDrag:false,weight:-1,shrinkWrap:3,iconAlign:"left",titleAlign:"left",titlePosition:0,titleRotation:"default",autoEl:{role:"presentation"},beforeRenderConfig:{glyph:null,icon:null,iconCls:null,iconAlign:null,title:{$value:{xtype:"title",flex:1},merge:function(b,a){if(typeof b!=="object"){b={text:b}}return Ext.merge(a?Ext.Object.chain(a):{},b)}},titleAlign:null,titlePosition:null,titleRotation:null},headerCls:"x-header",initComponent:function(){var d=this,c=d.items,b=d.itemPosition,a=[d.headerCls];d.tools=d.tools||[];d.items=c=(c?c.slice():[]);if(b!==undefined){d._userItems=c.slice();d.items=c=[]}d.indicateDragCls=d.headerCls+"-draggable";if(d.indicateDrag){a.push(d.indicateDragCls)}d.addCls(a);d.syncNoBorderCls();d.enableFocusableContainer=!d.isAccordionHeader&&d.tools.length>0;if(d.enableFocusableContainer){d.ariaRole="toolbar"}Ext.Array.push(c,d.tools);d.tools.length=0;Ext.panel.Bar.prototype.initComponent.call(this);d.on({dblclick:d.onDblClick,click:d.onClick,element:"el",scope:d})},addTool:function(a){var b=this;b.add(Ext.ComponentManager.create(a,"tool"));if(!b.isAccordionHeader&&b.tools.length>0&&!b.enableFocusableContainer){b.enableFocusableContainer=true;b.ariaRole="toolbar";if(b.rendered){b.ariaEl.dom.setAttribute("role","toolbar");b.initFocusableContainer(true)}}},afterLayout:function(){var b=this,e,a,c,d;if(b.vertical){a=b.frameTR;if(a){e=b.frameBR;c=b.frameTL;d=(b.getWidth()-a.getPadding("r")-((c)?c.getPadding("l"):b.el.getBorderWidth("l")))+"px";e.setStyle("background-position-x",d);a.setStyle("background-position-x",d)}}Ext.panel.Bar.prototype.afterLayout.call(this)},applyTitle:function(e,c){var d=this,a,b;e=e||"";a=typeof e==="string";if(a){e={text:e}}if(c){Ext.suspendLayouts();c.setConfig(e);Ext.resumeLayouts(true);e=c}else{if(a){e.xtype="title"}e.ui=d.ui;b=("rotation" in e);e.id=d.id+"-title";if(d.isAccordionHeader){e.ariaRole="tab";e.textElRole=null;e.focusable=true}e=Ext.create(e);if(!b&&d.vertical&&d.titleRotation==="default"){e.rotation=1}}return e},applyTitlePosition:function(b){var a=this.items.getCount();if(this._titleInItems){--a}return Math.max(Math.min(b,a),0)},beforeLayout:function(){Ext.panel.Bar.prototype.beforeLayout.call(this);this.syncBeforeAfterTitleClasses()},beforeRender:function(){var b=this,a=b.itemPosition;b.protoEl.unselectable();Ext.panel.Bar.prototype.beforeRender.call(this);if(a!==undefined){b.insert(a,b._userItems)}},getTools:function(){return this.tools.slice()},onAdd:function(b,a){var c=this.tools;Ext.panel.Bar.prototype.onAdd.call(this,b,a);if(b.isTool){c.push(b);c[b.type]=b}},onAdded:function(b,c,a){this.syncNoBorderCls();Ext.panel.Bar.prototype.onAdded.call(this,b,c,a)},onRemoved:function(b,c,a){this.syncNoBorderCls();Ext.panel.Bar.prototype.onRemoved.call(this,b,c,a)},setDock:function(c){var b=this,e=b.getTitle(),a=b.getTitleRotation(),d=e.getRotation();Ext.suspendLayouts();Ext.panel.Bar.prototype.setDock.call(this,c);if(a==="default"){a=(b.vertical?1:0);if(a!==d){e.setRotation(a)}if(b.rendered){b.resetItemMargins()}}Ext.resumeLayouts(true)},updateGlyph:function(a){this.getTitle().setGlyph(a)},updateIcon:function(a){this.getTitle().setIcon(a)},updateIconAlign:function(b,a){this.getTitle().setIconAlign(b)},updateIconCls:function(a){this.getTitle().setIconCls(a)},updateTitle:function(b,a){if(!a){this.insert(this.getTitlePosition(),b);this._titleInItems=true}this.titleCmp=b},updateTitleAlign:function(b,a){this.getTitle().setTextAlign(b)},updateTitlePosition:function(a){this.insert(a,this.getTitle())},updateTitleRotation:function(a){if(a==="default"){a=(this.vertical?1:0)}this.getTitle().setRotation(a)},privates:{fireClickEvent:function(a,c){var b="."+Ext.panel.Tool.prototype.baseCls;if(!c.getTarget(b)){this.fireEvent(a,this,c)}},getFramingInfoCls:function(){var c=this,b=Ext.panel.Bar.prototype.getFramingInfoCls.call(this),a=c.ownerCt;if(!c.expanding&&a&&(a.collapsed||c.isCollapsedExpander)){b+="-"+a.collapsedCls}return b+"-"+c.dock},onClick:function(a){this.fireClickEvent("click",a)},onDblClick:function(a){this.fireClickEvent("dblclick",a)},onFocusableContainerMousedown:function(c,b){var a=Ext.Component.fromElement(b);if(a===this){c.preventDefault()}else{this.mixins.focusablecontainer.onFocusableContainerMousedown.apply(this,arguments)}},syncBeforeAfterTitleClasses:function(c){var k=this,j=k.items,g=j.items,b=k.getTitlePosition(),a=g.length,h=j.generation,l=k.syncBeforeAfterGen,n,e,d,m;if(!c&&(l===h)){return}k.syncBeforeAfterGen=h;for(d=0;db){if(l){m.removeCls(e)}m.addCls(n)}}}},syncNoBorderCls:function(){var b=this,a=this.ownerCt,c=b.headerCls+"-noborder";if(a?(a.border===false&&!a.frame):b.border===false){b.addCls(c)}else{b.removeCls(c)}}}},0,["header"],["component","box","container","header"],{component:true,box:true,container:true,header:true},["widget.header"],[[Ext.util.FocusableContainer.prototype.mixinId||Ext.util.FocusableContainer.$className,Ext.util.FocusableContainer]],[Ext.panel,"Header"],0));(Ext.cmd.derive("Ext.layout.container.boxOverflow.None",Ext.Base,{alternateClassName:"Ext.layout.boxOverflow.None",factoryConfig:{defaultType:"none"},isBoxOverflowHandler:true,$configPrefixed:false,$configStrict:false,constructor:function(a){this.initConfig(a)},handleOverflow:Ext.emptyFn,clearOverflow:Ext.emptyFn,beginLayout:Ext.emptyFn,beginLayoutCycle:Ext.emptyFn,calculate:function(b){var a=this,c=b.state.boxPlan,d;if(c&&c.tooNarrow){d=a.handleOverflow(b);if(d){if(d.reservedSpace){a.layout.publishInnerCtSize(b,d.reservedSpace)}}}else{a.clearOverflow()}},completeLayout:Ext.emptyFn,finishedLayout:function(d){var c=this,a=c.layout.owner,b,e;if(a.hasListeners.overflowchange){b=a.query(">[hidden]");e=b.length;if(e!==c.lastHiddenCount){a.fireEvent("overflowchange",c.lastHiddenCount,e,b);c.lastHiddenCount=e}}},onRemove:Ext.emptyFn,getItem:function(a){return this.layout.owner.getComponent(a)},getOwnerType:function(a){var b;if(a.isToolbar){b="toolbar"}else{if(a.isTabBar){b="tab-bar"}else{if(a.isMenu){b="menu"}else{if(a.isBreadcrumb){b="breadcrumb"}else{b=a.getXType()}}}}return b},getPrefixConfig:Ext.emptyFn,getSuffixConfig:Ext.emptyFn,getOverflowCls:function(){return""},setVertical:function(){var b=this,a=b.layout,c=a.innerCt;c.removeCls(b.getOverflowCls(a.oppositeDirection));c.addCls(b.getOverflowCls(a.direction))}},1,0,0,0,["box.overflow.None","box.overflow.none"],[[Ext.mixin.Factoryable.prototype.mixinId||Ext.mixin.Factoryable.$className,Ext.mixin.Factoryable]],[Ext.layout.container.boxOverflow,"None",Ext.layout.boxOverflow,"None"],0));(Ext.cmd.derive("Ext.layout.container.boxOverflow.Scroller",Ext.layout.container.boxOverflow.None,{alternateClassName:"Ext.layout.boxOverflow.Scroller",animateScroll:false,scrollIncrement:20,wheelIncrement:10,scrollRepeatInterval:60,scrollDuration:400,scrollerCls:"x-box-scroller",beforeSuffix:"-before-scroller",afterSuffix:"-after-scroller",constructor:function(a){var b=this;b.mixins.observable.constructor.call(b,a);b.scrollPosition=0;b.scrollSize=0},getPrefixConfig:function(){return{role:"presentation",id:this.layout.owner.id+this.beforeSuffix,cls:this.createScrollerCls("beforeX"),style:"display:none"}},getSuffixConfig:function(){return{role:"presentation",id:this.layout.owner.id+this.afterSuffix,cls:this.createScrollerCls("afterX"),style:"display:none"}},createScrollerCls:function(e){var h=this,g=h.layout,b=g.owner,d=h.getOwnerType(b),a=h.scrollerCls,c=a+" "+a+"-"+g.names[e]+" "+a+"-"+d+" "+a+"-"+d+"-"+b.ui;if(b.plain){c+=" "+a+"-plain"}return c},getOverflowCls:function(a){return this.scrollerCls+"-body-"+a},beginLayout:function(a){a.innerCtScrollPos=this.getScrollPosition();Ext.layout.container.boxOverflow.None.prototype.beginLayout.apply(this,arguments)},finishedLayout:function(d){var c=this,g=d.state.boxPlan,b=c.layout,e=b.names,h=Math.min(c.getMaxScrollPosition(),d.innerCtScrollPos),a;if(g&&g.tooNarrow){a=d.childItems[d.childItems.length-1].props;c.scrollSize=a[e.x]+a[e.width];c.updateScrollButtons();b.innerCt[e.setScrollLeft](h)}Ext.layout.container.boxOverflow.None.prototype.finishedLayout.call(this,d)},handleOverflow:function(b){var h=this,g=h.layout.names,i=g.getWidth,j=g.parallelMargins,c,e,d,a;h.showScrollers();d=h.getBeforeScroller();a=h.getAfterScroller();c=d[i]()+a[i]()+d.getMargin(j)+a.getMargin(j);e=b.targetContext.getPaddingInfo()[g.width];return{reservedSpace:Math.max(c-e,0)}},getBeforeScroller:function(){var a=this;return a._beforeScroller||(a._beforeScroller=a.createScroller(a.beforeSuffix,"beforeRepeater","scrollLeft"))},getAfterScroller:function(){var a=this;return a._afterScroller||(a._afterScroller=a.createScroller(a.afterSuffix,"afterRepeater","scrollRight"))},createScroller:function(g,d,h){var e=this,c=e.layout.owner,b=e.scrollerCls,a;a=c.el.getById(c.id+g);a.addClsOnOver(b+"-hover");a.addClsOnClick(b+"-pressed");a.setVisibilityMode(Ext.Element.DISPLAY);e[d]=new Ext.util.ClickRepeater(a,{interval:e.scrollRepeatInterval,handler:h,scope:e});return a},createWheelListener:function(){var a=this;a.wheelListener=a.layout.innerCt.on("mousewheel",a.onMouseWheel,a,{destroyable:true})},onMouseWheel:function(a){a.stopEvent();this.scrollBy(this.getWheelDelta(a)*this.wheelIncrement*-1,false)},getWheelDelta:function(a){return a.getWheelDelta()},clearOverflow:function(){this.hideScrollers()},showScrollers:function(){var a=this;if(!a.wheelListener){a.createWheelListener()}a.getBeforeScroller().show();a.getAfterScroller().show();a.layout.owner.addClsWithUI(a.layout.direction==="vertical"?"vertical-scroller":"scroller")},hideScrollers:function(){var b=this,c=b.getBeforeScroller(),a=b.getAfterScroller();if(c){c.hide();a.hide();b.layout.owner.removeClsWithUI(b.layout.direction==="vertical"?"vertical-scroller":"scroller")}},destroy:function(){Ext.destroyMembers(this,"beforeRepeater","afterRepeater","_beforeScroller","_afterScroller","wheelListener");this.callParent()},scrollBy:function(b,a){this.scrollTo(this.getScrollPosition()+b,a)},getScrollAnim:function(){return{duration:this.scrollDuration,callback:this.updateScrollButtons,scope:this}},updateScrollButtons:function(){var b=this,d=b.getBeforeScroller(),a=b.getAfterScroller(),c;if(!d||!a){return}c=b.scrollerCls+"-disabled";d[b.atExtremeBefore()?"addCls":"removeCls"](c);a[b.atExtremeAfter()?"addCls":"removeCls"](c);b.scrolling=false},scrollLeft:function(){this.scrollBy(-this.scrollIncrement,false)},scrollRight:function(){this.scrollBy(this.scrollIncrement,false)},getScrollPosition:function(){var c=this,b=c.layout,a;if(isNaN(c.scrollPosition)){a=b.innerCt[b.names.getScrollLeft]()}else{a=c.scrollPosition}return a},getMaxScrollPosition:function(){var b=this,a=b.layout,c=b.scrollSize-a.innerCt.lastBox[a.names.width];return(c<0)?0:c},atExtremeBefore:function(){return !this.getScrollPosition()},atExtremeAfter:function(){return this.getScrollPosition()>=this.getMaxScrollPosition()},setVertical:function(){var c=this,d=c.getBeforeScroller(),b=c.getAfterScroller(),e=c.layout.names,a=c.scrollerCls;d.removeCls(a+"-"+e.beforeY);b.removeCls(a+"-"+e.afterY);d.addCls(a+"-"+e.beforeX);b.addCls(a+"-"+e.afterX);Ext.layout.container.boxOverflow.None.prototype.setVertical.call(this)},scrollTo:function(a,b){var g=this,e=g.layout,h=e.names,d=g.getScrollPosition(),c=Ext.Number.constrain(a,0,g.getMaxScrollPosition());if(c!==d&&!g.scrolling){g.scrollPosition=NaN;if(b===undefined){b=g.animateScroll}e.innerCt[h.scrollTo](h.beforeScrollX,c,b?g.getScrollAnim():false);if(b){g.scrolling=true}else{g.updateScrollButtons()}g.fireEvent("scroll",g,c,b?g.getScrollAnim():false)}},scrollToItem:function(k,c){var j=this,g=j.layout,d=g.owner,i=g.names,b=g.innerCt,a,e,h;k=j.getItem(k);if(k!==undefined){if(k===d.items.first()){h=0}else{if(k===d.items.last()){h=j.getMaxScrollPosition()}else{a=j.getItemVisibility(k);if(!a.fullyVisible){e=k.getBox(false,true);h=e[i.x];if(a.hiddenEnd){h-=(b[i.getWidth]()-e[i.width])}}}}if(h!==undefined){j.scrollTo(h,c)}}},getItemVisibility:function(j){var h=this,b=h.getItem(j).getBox(true,true),c=h.layout,g=c.names,e=b[g.x],d=e+b[g.width],a=h.getScrollPosition(),i=a+c.innerCt[g.getWidth]();return{hiddenStart:ei,fullyVisible:e>=a&&d<=i}}},1,0,0,0,["box.overflow.Scroller","box.overflow.scroller"],[["observable",Ext.mixin.Observable]],[Ext.layout.container.boxOverflow,"Scroller",Ext.layout.boxOverflow,"Scroller"],0));(Ext.cmd.derive("Ext.dd.DragDropManager",Ext.Base,{singleton:true,alternateClassName:["Ext.dd.DragDropMgr","Ext.dd.DDM"],ids:{},handleIds:{},dragCurrent:null,dragOvers:{},deltaX:0,deltaY:0,preventDefault:true,stopPropagation:true,initialized:false,locked:false,init:function(){this.initialized=true},POINT:0,INTERSECT:1,mode:0,notifyOccluded:false,dragCls:"x-dd-drag-current",_execOnAll:function(c,b){var e=this.ids,d,a,h,g;for(d in e){if(e.hasOwnProperty(d)){g=e[d];for(a in g){if(g.hasOwnProperty(a)){h=g[a];if(!this.isTypeOfDD(h)){continue}h[c].apply(h,b)}}}}},addListeners:function(){var a=this;a.init();Ext.getDoc().on({mouseup:a.handleMouseUp,mousemove:{fn:a.handleMouseMove,capture:false},dragstart:a.preventDrag,drag:a.preventDrag,dragend:a.preventDrag,capture:true,scope:a});Ext.getWin().on({unload:a._onUnload,resize:a._onResize,scope:a})},preventDrag:function(a){if(this.isMouseDown){a.stopPropagation()}},_onResize:function(a){this._execOnAll("resetConstraints",[])},lock:function(){this.locked=true},unlock:function(){this.locked=false},isLocked:function(){return this.locked},locationCache:{},useCache:true,clickPixelThresh:8,dragThreshMet:false,clickTimeout:null,startX:0,startY:0,regDragDrop:function(b,a){if(!this.initialized){this.init()}if(!this.ids[a]){this.ids[a]={}}this.ids[a][b.id]=b},removeDDFromGroup:function(c,a){if(!this.ids[a]){this.ids[a]={}}var b=this.ids[a];if(b&&b[c.id]){delete b[c.id]}},_remove:function(h,b){var e=this,c=e.ids,a=h.groups,d;if(e.clearingAll){return}if(e.dragCurrent===h){e.dragCurrent=null}for(d in a){if(a.hasOwnProperty(d)){if(b){delete c[d]}else{if(c[d]){delete c[d][h.id]}}}}delete e.handleIds[h.id];delete e.locationCache[h.id]},regHandle:function(b,a){if(!this.handleIds[b]){this.handleIds[b]={}}this.handleIds[b][a]=a},isDragDrop:function(a){return(this.getDDById(a))?true:false},getRelated:function(g,b){var e=[],d,c,a;for(d in g.groups){for(c in this.ids[d]){a=this.ids[d][c];if(!this.isTypeOfDD(a)){continue}if(!b||a.isTarget){e[e.length]=a}}}return e},isLegalTarget:function(e,d){var b=this.getRelated(e,true),c,a;for(c=0,a=b.length;ch.clickPixelThresh||c>h.clickPixelThresh){h.startDrag(h.startX,h.startY)}}if(h.dragThreshMet){i.b4Drag(j);i.onDrag(j);if(!i.moveOnly){h.fireEvents(j,false)}}h.stopEvent(j);return true},fireEvents:function(x,n){var z=this,o=Ext.supports.Touch,h=z.dragCurrent,v=z.currentPoint,r=v.x,q=v.y,p=[],j=[],l=[],b=[],y=[],w=[],a=o?document.documentElement.clientWidth/window.innerWidth:1,d,g,m,c,t,u,s,k;if(!h||h.isLocked()){return}k=!(h.deltaX<0||h.deltaY<0);if(o||(!z.notifyOccluded&&(!Ext.supports.CSSPointerEvents||Ext.isIE10m||Ext.isOpera)&&k)){d=h.getDragEl();if(k){d.style.visibility="hidden"}x.target=document.elementFromPoint(r/a,q/a);if(k){d.style.visibility="visible"}}for(t in z.dragOvers){g=z.dragOvers[t];delete z.dragOvers[t];if(!z.isTypeOfDD(g)||g.destroyed){continue}if(z.notifyOccluded){if(!this.isOverTarget(v,g,z.mode)){l.push(g)}}else{if(!x.within(g.getEl())){l.push(g)}}j[t]=true}for(s in h.groups){if("string"!==typeof s){continue}for(t in z.ids[s]){g=z.ids[s][t];if(z.isTypeOfDD(g)&&(m=g.getEl())&&(g.isTarget)&&(!g.isLocked())&&(Ext.fly(m).isVisible(true))&&((g!==h)||(h.ignoreSelf===false))){if(z.notifyOccluded){if((g.zIndex=z.getZIndex(m))!==-1){c=true}p.push(g)}else{if(x.within(g.getEl())){p.push(g);break}}}}}if(c){Ext.Array.sort(p,z.byZIndex)}for(t=0,u=p.length;t','",""],isSplitter:true,baseCls:"x-splitter",collapsedClsInternal:"x-splitter-collapsed",canResize:true,collapsible:null,collapseOnDblClick:true,defaultSplitMin:40,defaultSplitMax:1000,collapseTarget:"next",horizontal:false,vertical:false,size:5,tracker:null,ariaRole:"separator",focusable:true,tabIndex:0,getTrackerConfig:function(){return Ext.apply({xclass:"Ext.resizer.SplitterTracker",el:this.el,splitter:this},this.tracker)},beforeRender:function(){var c=this,d=c.getCollapseTarget(),b=c.collapsible,a;Ext.Component.prototype.beforeRender.call(this);if(d.collapsed){c.addCls(c.collapsedClsInternal)}if(!c.canResize){c.addCls(c.baseCls+"-noresize")}Ext.applyIf(c.renderData,{collapseDir:c.getCollapseDirection(),collapsible:(b!==null)?b:d.collapsible});c.ariaRenderAttributes=c.ariaRenderAttributes||{};c.ariaRenderAttributes["aria-orientation"]=c.orientation;c.protoEl.unselectable()},onRender:function(){var b=this,a;Ext.Component.prototype.onRender.apply(this,arguments);if(b.performCollapse!==false){if(b.renderData.collapsible){b.mon(b.collapseEl,"click",b.toggleTargetCmp,b)}if(b.collapseOnDblClick){b.mon(b.el,"dblclick",b.toggleTargetCmp,b)}}b.getCollapseTarget().on({collapse:b.onTargetCollapse,expand:b.onTargetExpand,beforeexpand:b.onBeforeTargetExpand,beforecollapse:b.onBeforeTargetCollapse,scope:b});if(b.canResize){b.tracker=Ext.create(b.getTrackerConfig());b.relayEvents(b.tracker,["beforedragstart","dragstart","dragend"])}a=b.collapseEl;if(a){a.lastCollapseDirCls=b.collapseDirProps[b.collapseDirection].cls}},getCollapseDirection:function(){var g=this,c=g.collapseDirection,e,a,b,d;if(!c){e=g.collapseTarget;if(e.isComponent){c=e.collapseDirection}if(!c){d=g.ownerCt.layout.type;if(e.isComponent){b=g.ownerCt.items;a=Number(b.indexOf(e)===b.indexOf(g)-1)<<1|Number(d==="hbox")}else{a=Number(g.collapseTarget==="prev")<<1|Number(d==="hbox")}c=["bottom","right","top","left"][a]}g.collapseDirection=c}g.setOrientation((c==="top"||c==="bottom")?"horizontal":"vertical");return c},getCollapseTarget:function(){var a=this;return a.collapseTarget.isComponent?a.collapseTarget:a.collapseTarget==="prev"?a.previousSibling():a.nextSibling()},setCollapseEl:function(b){var a=this.collapseEl;if(a){a.setDisplayed(b)}},onBeforeTargetExpand:function(a){this.setCollapseEl("none")},onBeforeTargetCollapse:function(){this.setCollapseEl("none")},onTargetCollapse:function(b){var a=this;if(b===a.getCollapseTarget()&&b[a.orientation==="vertical"?"collapsedHorizontal":"collapsedVertical"]()){a.el.addCls(a.collapsedClsInternal+" "+(a.collapsedCls||""))}a.setCollapseEl("")},onTargetExpand:function(b){var a=this;a.el.removeCls(a.collapsedClsInternal+" "+(a.collapsedCls||""));a.setCollapseEl("")},collapseDirProps:{top:{cls:"x-layout-split-top"},right:{cls:"x-layout-split-right"},bottom:{cls:"x-layout-split-bottom"},left:{cls:"x-layout-split-left"}},orientationProps:{horizontal:{opposite:"vertical",fixedAxis:"height",stretchedAxis:"width"},vertical:{opposite:"horizontal",fixedAxis:"width",stretchedAxis:"height"}},applyCollapseDirection:function(){var c=this,b=c.collapseEl,d=c.collapseDirProps[c.collapseDirection],a;if(b){a=b.lastCollapseDirCls;if(a){b.removeCls(a)}b.addCls(b.lastCollapseDirCls=d.cls)}},applyOrientation:function(){var e=this,c=e.orientation,d=e.orientationProps[c],g=e.size,b=d.fixedAxis,h=d.stretchedAxis,a=e.baseCls+"-";e[c]=true;e[d.opposite]=false;if(!e.hasOwnProperty(b)||e[b]==="100%"){e[b]=g}if(!e.hasOwnProperty(h)||e[h]===g){e[h]="100%"}e.removeCls(a+d.opposite);e.addCls(a+c)},setOrientation:function(a){var b=this;if(b.orientation!==a){b.orientation=a;b.applyOrientation()}},updateOrientation:function(){delete this.collapseDirection;this.getCollapseDirection();this.applyCollapseDirection()},toggleTargetCmp:function(d,b){var c=this.getCollapseTarget(),g=c.placeholder,a;if(Ext.isFunction(c.expand)&&Ext.isFunction(c.collapse)){if(g&&!g.hidden){a=true}else{a=!c.hidden}if(a){if(c.collapsed){c.expand()}else{if(c.collapseDirection){c.collapse()}else{c.collapse(this.renderData.collapseDir)}}}}},setSize:function(){var a=this;Ext.Component.prototype.setSize.apply(this,arguments);if(Ext.isIE&&a.el){a.el.repaint()}},beforeDestroy:function(){Ext.destroy(this.tracker);Ext.Component.prototype.beforeDestroy.call(this)}},0,["splitter"],["component","box","splitter"],{component:true,box:true,splitter:true},["widget.splitter"],0,[Ext.resizer,"Splitter"],0));(Ext.cmd.derive("Ext.layout.container.Box",Ext.layout.container.Container,{alternateClassName:"Ext.layout.BoxLayout",type:"box",config:{align:"begin",constrainAlign:false,enableSplitters:true,overflowHandler:{$value:null,merge:function(b,a){if(typeof b==="string"){b={type:b}}return Ext.merge(a?Ext.Object.chain(a):{},b)}},padding:0,pack:"start",stretchMaxPartner:undefined,vertical:false,alignRoundingMethod:"round"},itemCls:"x-box-item",targetCls:"x-box-layout-ct",targetElCls:"x-box-target",innerCls:"x-box-inner",manageMargins:true,createsInnerCt:true,childEls:["innerCt","targetEl"],renderTpl:['{%var oc,l=values.$comp.layout,oh=l.overflowHandler;if (oh && oh.getPrefixConfig!==Ext.emptyFn) {if(oc=oh.getPrefixConfig())dh.generateMarkup(oc, out)}%}{%if (oh && oh.getSuffixConfig!==Ext.emptyFn) {if(oc=oh.getSuffixConfig())dh.generateMarkup(oc, out)}%}',{disableFormats:true,definitions:"var dh=Ext.DomHelper;"}],constructor:function(a){var c=this,b;Ext.layout.container.Container.prototype.constructor.apply(this,arguments);c.setVertical(c.vertical);c.flexSortFn=c.flexSort.bind(c);b=typeof c.padding;if(b==="string"||b==="number"){c.padding=Ext.util.Format.parseBox(c.padding);c.padding.height=c.padding.top+c.padding.bottom;c.padding.width=c.padding.left+c.padding.right}},_beginRe:/^(?:begin|left|top)$/,_centerRe:/^(?:center|middle)$/,_endRe:/^(?:end|right|bottom)$/,_percentageRe:/^\s*(\d+(?:\.\d*)?)\s*[%]\s*$/,getItemSizePolicy:function(q,r){var l=this,j=l.sizePolicy,h=l.align,g=q.flex,o=h,k=l.names,i=k.height,n=k.width,b=q[n],p=q[i],d=l._percentageRe,c=d.test(b),e=(h==="stretch"),a=(h==="stretchmax"),m=l.constrainAlign;if(!r&&(e||g||c||(m&&!a))){r=l.owner.getSizeModel()}if(e){if(!d.test(p)&&r[i].shrinkWrap){o="stretchmax"}}else{if(!a){if(d.test(p)){o="stretch"}else{if(m&&!r[i].shrinkWrap){o="stretchmax"}else{o=""}}}}if(g||c){if(!r[n].shrinkWrap){j=j.flex}}return j[o]},flexSort:function(o,n){var l=this.names.maxWidth,e=this.names.minWidth,m=Infinity,k=o.target,r=n.target,i=k.flex,h=r.flex,s=0,c,p,j,d,q,g;j=k[l]||m;d=r[l]||m;c=k[e]||0;p=r[e]||0;q=isFinite(c)||isFinite(p);g=isFinite(j)||isFinite(d);if(q||g){if(g){s=j-d}if(s===0&&q){s=p-c}if(s===0){if(g){s=h-i}else{s=i-h}}}return s},isItemBoxParent:function(a){return true},isItemShrinkWrap:function(a){return true},roundFlex:function(a){return Math.floor(a)},beginCollapse:function(b){var a=this;if(a.direction==="vertical"&&b.collapsedVertical()){b.collapseMemento.capture(["flex"]);delete b.flex}else{if(a.direction==="horizontal"&&b.collapsedHorizontal()){b.collapseMemento.capture(["flex"]);delete b.flex}}},beginExpand:function(a){a.collapseMemento.restore(["flex"])},beginLayout:function(e){var i=this,b=i.owner,d=b.stretchMaxPartner,a=i.innerCt.dom.style,h=i.names,g=i.overflowHandler,j=b.getScrollable(),c;e.boxNames=h;if(g){g.beginLayout(e)}if(typeof d==="string"){d=Ext.getCmp(d)||b.query(d)[0]}e.stretchMaxPartner=d&&e.context.getCmp(d);Ext.layout.container.Container.prototype.beginLayout.apply(this,arguments);e.innerCtContext=e.getEl("innerCt",i);e.targetElContext=e.getEl("targetEl",i);if(j){e.ownerScrollable=j;c=j.getPosition();if(c.x||c.y){e.scrollRestore=c}}a.width=a.height=""},beginLayoutCycle:function(d,n){var k=this,a=d.state,l=d.ownerScrollable,i=k.align,j=d.boxNames,m=k.pack,c=k._centerRe,e=k.overflowHandler,b=d.state.canScroll,h,g;if(e){e.beginLayoutCycle(d,n)}Ext.layout.container.Container.prototype.beginLayoutCycle.apply(this,arguments);d.parallelSizeModel=h=d[j.widthModel];d.perpendicularSizeModel=g=d[j.heightModel];d.boxOptions={align:i={stretch:i==="stretch",stretchmax:i==="stretchmax",center:c.test(i),bottom:k._endRe.test(i)},pack:m={center:c.test(m),end:m==="end"}};if(l){if(!b){a.canScroll={parallel:!h.shrinkWrap&&l[j.getX](),perpendicular:!g.shrinkWrap&&l[j.getY]()}}if(!a.actualScroll){a.actualScroll={parallel:false,perpendicular:false}}}if(i.stretch&&g.shrinkWrap){i.stretchmax=true;i.stretch=false}i.nostretch=!(i.stretch||i.stretchmax);if(h.shrinkWrap){m.center=m.end=false}k.cacheFlexes(d);k.targetEl.setWidth(20000)},cacheFlexes:function(m){var B=this,n=m.boxNames,a=n.widthModel,h=n.heightModel,c=m.boxOptions.align.nostretch,t=0,b=m.childItems,v=b.length,z=[],o=0,w=0,s=0,l=n.minWidth,y=n.minHeight,j=B._percentageRe,x=0,A=0,g,q,u,k,d,e,r,p;while(v--){q=b[v];g=q.target;e=q[a];if(e.calculated){q.flex=u=g.flex;if(u){t+=u;z.push(q);o+=g[l]||0}else{k=j.exec(g[n.width]);q.percentageParallel=parseFloat(k[1])/100;++x}}if(e.configured){r=g[n.width]}else{r=g[l]||0}s+=r;d=q[h];if(c&&d.calculated){k=j.exec(g[n.height]);q.percentagePerpendicular=parseFloat(k[1])/100;++A}if(d.configured){p=g[n.height]}else{p=g[y]||0}if(p>w){w=p}}m.flexedItems=z;m.flexedMinWidth=o;m.smallestWidth=s;m.smallestHeight=w;m.totalFlex=t;m.percentageWidths=x;m.percentageHeights=A;Ext.Array.sort(z,B.flexSortFn)},calculate:function(c){var h=this,g=c.boxNames,a=c.state,e=a.actualScroll,j=a.needsScroll,b=a.canScroll,i=a.boxPlan||(a.boxPlan={}),d=h.overflowHandler;i.targetSize=h.getContainerSize(c);if(b&&!j){a.needsScroll=j={parallel:b.parallel&&i.targetSize[g.width]o){r.invalidate({before:F,after:C,layout:t,childHeight:o,names:M});v.state.parallelDone=false}if(isNaN(H=m(H,E+w,r.target[M.minHeight]||0))){return false}}}if(L){H+=a;v[M.hasOverflowX]=true;v.target.componentLayout[M.setHeightInDom]=true;v[M.invalidateScrollX]=Ext.isIE8}e=v.stretchMaxPartner;if(e){v.setProp("maxChildHeight",H);N=e.childItems;if(N&&N.length){H=m(H,e.getProp("maxChildHeight"));if(isNaN(H)){return false}}}v[M.setContentHeight](H+t.padding[l]+v.targetContext.getPaddingInfo()[l]);if(L){H-=a}if(H>b[l]&&u&&u.perpendicular){Q.actualScroll.perpendicular=true}B.maxSize=H;if(q){c=H}else{if(P||O||D){if(I){c=d?H:o}else{c=d?H:m(o,H)}c-=v.innerCtContext.getBorderInfo()[l]}}for(K=0;K0){z=k+Math[t.alignRoundingMethod](y/2)}}else{if(O){z=m(0,c-z-r.props[l])}}}r.setProp(s,z)}return true},onBeforeConstrainInvalidateChild:function(b,a){var c=a.names.heightModel;if(!b[c].constrainedMin){b[c]=Ext.layout.SizeModel.calculated}},onAfterConstrainInvalidateChild:function(b,a){var c=a.names;b.setProp(c.beforeY,0);if(b[c.heightModel].calculated){b[c.setHeight](a.childHeight)}},calculateStretchMax:function(c,k,m){var l=this,h=k.height,n=k.width,g=c.childItems,a=g.length,p=m.maxSize,o=l.onBeforeStretchMaxInvalidateChild,e=l.onAfterStretchMaxInvalidateChild,q,j,d,b;for(d=0;d":{xtype:"tbfill",height:0}},1:{"->":{xtype:"tbfill",width:0}}}},initComponent:function(){var c=this,b=c.layout,a=c.vertical;if(a===undefined){c.vertical=a=c.dock==="right"||c.dock==="left"}c.layout=b=Ext.applyIf(Ext.isString(b)?{type:b}:b||{},{type:a?"vbox":"hbox",align:a?"stretchmax":"middle"});if(c.overflowHandler){b.overflowHandler=c.overflowHandler}else{if(c.enableOverflow){b.overflowHandler="menu"}}if(a){c.addClsWithUI("vertical")}if(c.ui==="footer"){c.ignoreBorderManagement=true}Ext.container.Container.prototype.initComponent.call(this)},getRefItems:function(a){var e=this,b=Ext.container.Container.prototype.getRefItems.apply(this,arguments),d=e.layout,c;if(a&&(e.enableOverflow||(e.overflowHandler==="menu"))){c=d.overflowHandler;if(c&&c.menu){b=b.concat(c.menu.getRefItems(a))}}return b},lookupComponent:function(e){var d=arguments,a,b;if(typeof e==="string"){b=Ext.toolbar.Toolbar;a=b.shortcutsHV[this.vertical?1:0][e]||b.shortcuts[e];if(typeof a==="string"){e={xtype:a}}else{if(a){e=Ext.apply({},a)}else{e={xtype:"tbtext",text:e}}}this.applyDefaults(e);d=[e]}return Ext.container.Container.prototype.lookupComponent.apply(this,d)},onBeforeAdd:function(b){var c=this,d=c.ui==="footer",a=d?c.defaultFooterButtonUI:c.defaultButtonUI;if(b.isSegmentedButton){if(b.getDefaultUI()==="default"&&!b.config.hasOwnProperty("defaultUI")){b.setDefaultUI(a)}}else{if(b.ui==="default"&&!b.hasOwnProperty("ui")){if(b.isButton){b.ui=a}else{if(b.isFormField){b.ui=d?c.defaultFooterFieldUI:c.defaultFieldUI}}}}if(b instanceof Ext.toolbar.Separator){b.setUI(c.vertical?"vertical":"horizontal")}Ext.container.Container.prototype.onBeforeAdd.apply(this,arguments)},onAdd:function(a){if(a.needArrowKeys&&this.enableFocusableContainer){this.enableFocusableContainer=false}Ext.container.Container.prototype.onAdd.apply(this,arguments);this.trackMenu(a)},onRemove:function(a){Ext.container.Container.prototype.onRemove.apply(this,arguments);this.trackMenu(a,true)},privates:{applyDefaults:function(a){if(!Ext.isString(a)){a=Ext.container.Container.prototype.applyDefaults.apply(this,arguments)}return a},trackMenu:function(c,a){var b=this;if(b.trackMenus&&c.menu){c[a?"un":"on"]({mouseover:b.onButtonOver,menushow:b.onButtonMenuShow,menuhide:b.onButtonMenuHide,scope:b})}},getChildItemsToDisable:function(){return this.items.getRange()},onButtonOver:function(b,c){var a=this.activeMenuBtn;if(a&&a!==b){a.hideMenu();b.focus();b.showMenu(c);this.activeMenuBtn=b}},onButtonMenuShow:function(a){this.activeMenuBtn=a},onButtonMenuHide:function(a){this.activeMenuBtn=null}}},0,["toolbar"],["component","box","container","toolbar"],{component:true,box:true,container:true,toolbar:true},["widget.toolbar"],[[Ext.util.FocusableContainer.prototype.mixinId||Ext.util.FocusableContainer.$className,Ext.util.FocusableContainer]],[Ext.toolbar,"Toolbar",Ext,"Toolbar"],0));(Ext.cmd.derive("Ext.dd.DragDrop",Ext.Base,{constructor:function(c,a,b){if(c){this.init(c,a,b)}},id:null,config:null,dragElId:null,handleElId:null,invalidHandleTypes:null,invalidHandleIds:null,invalidHandleClasses:null,startPageX:0,startPageY:0,groups:null,locked:false,lock:function(){this.locked=true},moveOnly:false,unlock:function(){this.locked=false},isTarget:true,padding:null,_domRef:null,__ygDragDrop:true,constrainX:false,constrainY:false,minX:0,maxX:0,minY:0,maxY:0,maintainOffset:false,xTicks:null,yTicks:null,primaryButtonOnly:true,available:false,hasOuterHandles:false,triggerEvent:"mousedown",b4StartDrag:function(a,b){},startDrag:function(a,b){},b4Drag:function(a){},onDrag:function(a){},onDragEnter:function(a,b){},b4DragOver:function(a){},onDragOver:function(a,b){},b4DragOut:function(a){},onDragOut:function(a,b){},b4DragDrop:function(a){},onDragDrop:function(a,b){},onInvalidDrop:function(a){},b4EndDrag:function(a){},endDrag:function(a){},b4MouseDown:function(a){},onMouseDown:function(a){},onMouseUp:function(a){},onAvailable:function(){},defaultPadding:{left:0,right:0,top:0,bottom:0},constrainTo:function(e,b,l){if(Ext.isNumber(b)){b={left:b,right:b,top:b,bottom:b}}b=b||this.defaultPadding;var h=Ext.get(this.getEl()).getBox(),m=Ext.get(e),k=m.getScroll(),g,i=m.dom,j,d,a;if(i===document.body){g={x:k.left,y:k.top,width:Ext.Element.getViewportWidth(),height:Ext.Element.getViewportHeight()}}else{j=m.getXY();g={x:j[0],y:j[1],width:i.clientWidth,height:i.clientHeight}}d=h.y-g.y;a=h.x-g.x;this.resetConstraints();this.setXConstraint(a-(b.left||0),g.width-a-h.width-(b.right||0),this.xTickSize);this.setYConstraint(d-(b.top||0),g.height-d-h.height-(b.bottom||0),this.yTickSize)},getEl:function(){if(!this._domRef){this._domRef=Ext.getDom(this.id)}return this._domRef},getDragEl:function(){return Ext.getDom(this.dragElId)},init:function(d,a,b){var c=this;c.el=c.el||Ext.get(d);c.initTarget(d,a,b);Ext.get(c.id).on(c.triggerEvent,c.handleMouseDown,c)},initTarget:function(c,a,b){this.config=b||{};this.DDMInstance=Ext.dd.DragDropManager;this.groups={};if(typeof c!=="string"){c=Ext.id(c)}this.id=c;this.addToGroup((a)?a:"default");this.handleElId=c;this.setDragElId(c);this.invalidHandleTypes={A:"A"};this.invalidHandleIds={};this.invalidHandleClasses=[];this.applyConfig();this.handleOnAvailable()},applyConfig:function(){this.padding=this.config.padding||[0,0,0,0];this.isTarget=(this.config.isTarget!==false);this.maintainOffset=(this.config.maintainOffset);this.primaryButtonOnly=(this.config.primaryButtonOnly!==false)},handleOnAvailable:function(){this.available=true;this.resetConstraints();this.onAvailable()},setPadding:function(c,a,d,b){if(!a&&0!==a){this.padding=[c,c,c,c]}else{if(!d&&0!==d){this.padding=[c,a,c,a]}else{this.padding=[c,a,d,b]}}},setInitPosition:function(d,c){var e=this.getEl(),b,a,g;if(!this.DDMInstance.verifyEl(e)){return}b=d||0;a=c||0;g=Ext.fly(e).getXY();this.initPageX=g[0]-b;this.initPageY=g[1]-a;this.lastPageX=g[0];this.lastPageY=g[1];this.setStartPosition(g)},setStartPosition:function(b){var a=b||Ext.fly(this.getEl()).getXY();this.deltaSetXY=null;this.startPageX=a[0];this.startPageY=a[1]},addToGroup:function(a){this.groups[a]=true;this.DDMInstance.regDragDrop(this,a)},removeFromGroup:function(a){if(this.groups[a]){delete this.groups[a]}this.DDMInstance.removeDDFromGroup(this,a)},setDragElId:function(a){this.dragElId=a},setHandleElId:function(a){if(typeof a!=="string"){a=Ext.id(a)}this.handleElId=a;this.DDMInstance.regHandle(this.id,a)},setOuterHandleElId:function(a){if(typeof a!=="string"){a=Ext.id(a)}Ext.get(a).on(this.triggerEvent,this.handleMouseDown,this);this.setHandleElId(a);this.hasOuterHandles=true},unreg:function(){var b=this,a;if(b._domRef){a=Ext.fly(b.id);if(a){a.un(b.triggerEvent,b.handleMouseDown,b)}}b._domRef=null;b.DDMInstance._remove(b,b.autoGroup)},destroy:function(){this.unreg();this.callParent()},isLocked:function(){return(this.DDMInstance.isLocked()||this.locked)},handleMouseDown:function(c,b){var a=this;if((a.primaryButtonOnly&&c.button)||a.isLocked()){return}a.DDMInstance.refreshCache(a.groups);if(a.hasOuterHandles||a.DDMInstance.isOverTarget(c.getPoint(),a)){if(a.clickValidator(c)){a.setStartPosition();a.b4MouseDown(c);a.onMouseDown(c);a.DDMInstance.handleMouseDown(c,a);a.DDMInstance.stopEvent(c)}}},clickValidator:function(b){var a=b.getTarget();return(this.isValidHandleChild(a)&&(this.id===this.handleElId||this.DDMInstance.handleWasClicked(a,this.id)))},addInvalidHandleType:function(a){var b=a.toUpperCase();this.invalidHandleTypes[b]=b},addInvalidHandleId:function(a){if(typeof a!=="string"){a=Ext.id(a)}this.invalidHandleIds[a]=a},addInvalidHandleClass:function(a){this.invalidHandleClasses.push(a)},removeInvalidHandleType:function(a){var b=a.toUpperCase();delete this.invalidHandleTypes[b]},removeInvalidHandleId:function(a){if(typeof a!=="string"){a=Ext.id(a)}delete this.invalidHandleIds[a]},removeInvalidHandleClass:function(b){var d=this.invalidHandleClasses,a=d.length,c;for(c=0;c=this.minX;b=b-a){if(!c[b]){this.xTicks[this.xTicks.length]=b;c[b]=true}}for(b=this.initPageX;b<=this.maxX;b=b+a){if(!c[b]){this.xTicks[this.xTicks.length]=b;c[b]=true}}Ext.Array.sort(this.xTicks,this.DDMInstance.numericSort)},setYTicks:function(d,a){this.yTicks=[];this.yTickSize=a;var c={},b;for(b=this.initPageY;b>=this.minY;b=b-a){if(!c[b]){this.yTicks[this.yTicks.length]=b;c[b]=true}}for(b=this.initPageY;b<=this.maxY;b=b+a){if(!c[b]){this.yTicks[this.yTicks.length]=b;c[b]=true}}Ext.Array.sort(this.yTicks,this.DDMInstance.numericSort)},setXConstraint:function(c,b,a){this.leftConstraint=c;this.rightConstraint=b;this.minX=this.initPageX-c;this.maxX=this.initPageX+b;if(a){this.setXTicks(this.initPageX,a)}this.constrainX=true},clearConstraints:function(){this.constrainX=false;this.constrainY=false;this.clearTicks()},clearTicks:function(){this.xTicks=null;this.yTicks=null;this.xTickSize=0;this.yTickSize=0},setYConstraint:function(a,c,b){this.topConstraint=a;this.bottomConstraint=c;this.minY=this.initPageY-a;this.maxY=this.initPageY+c;if(b){this.setYTicks(this.initPageY,b)}this.constrainY=true},resetConstraints:function(){if(this.initPageX||this.initPageX===0){var b=(this.maintainOffset)?this.lastPageX-this.initPageX:0,a=(this.maintainOffset)?this.lastPageY-this.initPageY:0;this.setInitPosition(b,a)}else{this.setInitPosition()}if(this.constrainX){this.setXConstraint(this.leftConstraint,this.rightConstraint,this.xTickSize)}if(this.constrainY){this.setYConstraint(this.topConstraint,this.bottomConstraint,this.yTickSize)}},getTick:function(h,d){if(!d){return h}else{if(d[0]>=h){return d[0]}else{var b,a,c,g,e;for(b=0,a=d.length;b=h){g=h-d[b];e=d[c]-h;return(e>g)?d[b]:d[c]}}return d[d.length-1]}}},toString:function(){return("DragDrop "+this.id)}},3,0,0,0,0,0,[Ext.dd,"DragDrop"],0));(Ext.cmd.derive("Ext.dd.DD",Ext.dd.DragDrop,{constructor:function(c,a,b){if(c){this.init(c,a,b)}},scroll:true,autoOffset:function(c,b){var a=c-this.startPageX,d=b-this.startPageY;this.setDelta(a,d)},setDelta:function(b,a){this.deltaX=b;this.deltaY=a},setDragElPos:function(c,b){var a=this.getDragEl();this.alignElWithMouse(a,c,b)},alignElWithMouse:function(b,e,c){var g=this.getTargetCoord(e,c),d=b.dom?b:Ext.fly(b,"_dd"),l=d.getSize(),i=Ext.Element,j,a,k,h;if(!this.deltaSetXY){j=this.cachedViewportSize={width:i.getDocumentWidth(),height:i.getDocumentHeight()};a=[Math.max(0,Math.min(g.x,j.width-l.width)),Math.max(0,Math.min(g.y,j.height-l.height))];d.setXY(a);k=this.getLocalX(d);h=d.getLocalY();this.deltaSetXY=[k-g.x,h-g.y]}else{j=this.cachedViewportSize;this.setLocalXY(d,Math.max(0,Math.min(g.x+this.deltaSetXY[0],j.width-l.width)),Math.max(0,Math.min(g.y+this.deltaSetXY[1],j.height-l.height)))}this.cachePosition(g.x,g.y);this.autoScroll(g.x,g.y,b.offsetHeight,b.offsetWidth);return g},cachePosition:function(b,a){if(b){this.lastPageX=b;this.lastPageY=a}else{var c=Ext.fly(this.getEl()).getXY();this.lastPageX=c[0];this.lastPageY=c[1]}},autoScroll:function(l,k,e,m){if(this.scroll){var n=Ext.Element.getViewportHeight(),b=Ext.Element.getViewportWidth(),p=this.DDMInstance.getScrollTop(),d=this.DDMInstance.getScrollLeft(),j=e+k,o=m+l,i=(n+p-k-this.deltaY),g=(b+d-l-this.deltaX),c=40,a=(document.all)?80:30;if(j>n&&i0&&k-pb&&g0&&l-dthis.maxX){a=this.maxX}}if(this.constrainY){if(dthis.maxY){d=this.maxY}}a=this.getTick(a,this.xTicks);d=this.getTick(d,this.yTicks);return{x:a,y:d}},applyConfig:function(){Ext.dd.DragDrop.prototype.applyConfig.call(this);this.scroll=(this.config.scroll!==false)},b4MouseDown:function(b){var a=b.getXY();this.autoOffset(a[0],a[1])},b4Drag:function(b){var a=b.getXY();this.setDragElPos(a[0],a[1])},toString:function(){return("DD "+this.id)},getLocalX:function(a){return a.getLocalX()},setLocalXY:function(b,a,c){b.setLocalXY(a,c)}},3,0,0,0,0,0,[Ext.dd,"DD"],0));(Ext.cmd.derive("Ext.dd.DDProxy",Ext.dd.DD,{statics:{dragElId:"ygddfdiv"},constructor:function(c,a,b){if(c){this.init(c,a,b);this.initFrame()}},resizeFrame:true,centerFrame:false,createFrame:function(){var b=this,a=document.body,d,c;if(!a||!a.firstChild){Ext.defer(function(){b.createFrame()},50);return}d=this.getDragEl();if(!d){d=document.createElement("div");d.id=this.dragElId;d.setAttribute("role","presentation");c=d.style;c.position="absolute";c.visibility="hidden";c.cursor="move";c.border="2px solid #aaa";c.zIndex=999;a.insertBefore(d,a.firstChild)}},initFrame:function(){this.createFrame()},applyConfig:function(){Ext.dd.DD.prototype.applyConfig.call(this);this.resizeFrame=(this.config.resizeFrame!==false);this.centerFrame=(this.config.centerFrame);this.setDragElId(this.config.dragElId||Ext.dd.DDProxy.dragElId)},showFrame:function(e,d){var c=this,a=c.getDragEl(),b=a.style;c._resizeProxy();if(c.centerFrame){c.setDelta(Math.round(parseInt(b.width,10)/2),Math.round(parseInt(b.height,10)/2))}c.setDragElPos(e,d);Ext.fly(a).show()},_resizeProxy:function(){if(this.resizeFrame){var a=this.getEl();Ext.fly(this.getDragEl()).setSize(a.offsetWidth,a.offsetHeight)}},b4MouseDown:function(c){var b=c.getXY(),a=b[0],d=b[1];this.autoOffset(a,d);this.setDragElPos(a,d)},b4StartDrag:function(a,b){this.showFrame(a,b)},b4EndDrag:function(a){Ext.fly(this.getDragEl()).hide()},endDrag:function(c){var b=this.getEl(),a=this.getDragEl();a.style.visibility="";this.beforeMove();b.style.visibility="hidden";Ext.dd.DDM.moveToEl(b,a);a.style.visibility="hidden";b.style.visibility="";this.afterDrag()},beforeMove:function(){},afterDrag:function(){},toString:function(){return("DDProxy "+this.id)}},3,0,0,0,0,0,[Ext.dd,"DDProxy"],0));(Ext.cmd.derive("Ext.dd.StatusProxy",Ext.Component,{animRepair:false,childEls:["ghost"],renderTpl:[''],repairCls:"x-dd-drag-repair",ariaRole:"presentation",skipLayout:true,constructor:function(a){var b=this;a=a||{};Ext.apply(b,{hideMode:"visibility",hidden:true,floating:true,id:b.id||Ext.id(),cls:"x-dd-drag-proxy "+this.dropNotAllowed,shadow:a.shadow||false,renderTo:Ext.getDetachedBody()});Ext.Component.prototype.constructor.apply(this,arguments);this.dropStatus=this.dropNotAllowed},dropAllowed:"x-dd-drop-ok",dropNotAllowed:"x-dd-drop-nodrop",setStatus:function(a){a=a||this.dropNotAllowed;if(this.dropStatus!==a){this.el.replaceCls(this.dropStatus,a);this.dropStatus=a}},reset:function(b){var c=this,a="x-dd-drag-proxy ";c.el.replaceCls(a+c.dropAllowed,a+c.dropNotAllowed);c.dropStatus=c.dropNotAllowed;if(b){c.ghost.setHtml("")}},update:function(a){if(typeof a==="string"){this.ghost.setHtml(a)}else{this.ghost.setHtml("");a.style.margin="0";this.ghost.dom.appendChild(a)}var b=this.ghost.dom.firstChild;if(b){Ext.fly(b).setStyle("float","none")}},getGhost:function(){return this.ghost},hide:function(a){Ext.Component.prototype.hide.call(this);if(a){this.reset(true)}},stop:function(){if(this.anim&&this.anim.isAnimated&&this.anim.isAnimated()){this.anim.stop()}},sync:function(){this.el.syncUnderlays()},repair:function(c,d,a){var b=this;b.callback=d;b.scope=a;if(c&&b.animRepair!==false){b.el.addCls(b.repairCls);b.el.setUnderlaysVisible(false);b.anim=b.el.animate({duration:b.repairDuration||500,easing:"ease-out",to:{x:c[0],y:c[1]},stopAnimation:true,callback:b.afterRepair,scope:b})}else{b.afterRepair()}},afterRepair:function(){var a=this;a.hide(true);a.el.removeCls(a.repairCls);if(typeof a.callback==="function"){a.callback.call(a.scope||a)}delete a.callback;delete a.scope}},1,0,["component","box"],{component:true,box:true},0,0,[Ext.dd,"StatusProxy"],0));(Ext.cmd.derive("Ext.dd.DragSource",Ext.dd.DDProxy,{dropAllowed:"x-dd-drop-ok",dropNotAllowed:"x-dd-drop-nodrop",animRepair:true,repairHighlightColor:"c3daf9",constructor:function(b,a){this.el=Ext.get(b);if(!this.dragData){this.dragData={}}Ext.apply(this,a);if(!this.proxy){this.proxy=new Ext.dd.StatusProxy({id:this.el.id+"-drag-status-proxy",animRepair:this.animRepair})}Ext.dd.DDProxy.prototype.constructor.call(this,this.el.dom,this.ddGroup||this.group,{dragElId:this.proxy.id,resizeFrame:false,isTarget:false,scroll:this.scroll===true});this.dragging=false},getDragData:function(a){return this.dragData},onDragEnter:function(c,d){var b=Ext.dd.DragDropManager.getDDById(d),a;this.cachedTarget=b;if(this.beforeDragEnter(b,c,d)!==false){if(b.isNotifyTarget){a=b.notifyEnter(this,c,this.dragData);this.proxy.setStatus(a)}else{this.proxy.setStatus(this.dropAllowed)}if(this.afterDragEnter){this.afterDragEnter(b,c,d)}}},beforeDragEnter:function(b,a,c){return true},onDragOver:function(c,d){var b=this.cachedTarget||Ext.dd.DragDropManager.getDDById(d),a;if(this.beforeDragOver(b,c,d)!==false){if(b.isNotifyTarget){a=b.notifyOver(this,c,this.dragData);this.proxy.setStatus(a)}if(this.afterDragOver){this.afterDragOver(b,c,d)}}},beforeDragOver:function(b,a,c){return true},onDragOut:function(b,c){var a=this.cachedTarget||Ext.dd.DragDropManager.getDDById(c);if(this.beforeDragOut(a,b,c)!==false){if(a.isNotifyTarget){a.notifyOut(this,b,this.dragData)}this.proxy.reset();if(this.afterDragOut){this.afterDragOut(a,b,c)}}this.cachedTarget=null},beforeDragOut:function(b,a,c){return true},onDragDrop:function(b,c){var a=this.cachedTarget||Ext.dd.DragDropManager.getDDById(c);if(this.beforeDragDrop(a,b,c)!==false){if(a.isNotifyTarget){if(a.notifyDrop(this,b,this.dragData)!==false){this.onValidDrop(a,b,c)}else{this.onInvalidDrop(a,b,c)}}else{this.onValidDrop(a,b,c)}if(this.afterDragDrop){this.afterDragDrop(a,b,c)}}delete this.cachedTarget},beforeDragDrop:function(b,a,c){return true},onValidDrop:function(b,a,c){this.hideProxy();if(this.afterValidDrop){this.afterValidDrop(b,a,c)}},getRepairXY:function(b,a){return this.el.getXY()},onInvalidDrop:function(c,b,d){var a=this;if(!b){b=c;c=null;d=b.getTarget().id}if(a.beforeInvalidDrop(c,b,d)!==false){if(a.cachedTarget){if(a.cachedTarget.isNotifyTarget){a.cachedTarget.notifyOut(a,b,a.dragData)}a.cacheTarget=null}a.proxy.repair(a.getRepairXY(b,a.dragData),a.afterRepair,a);if(a.afterInvalidDrop){a.afterInvalidDrop(b,d)}}},afterRepair:function(){var a=this;if(Ext.enableFx){a.el.highlight(a.repairHighlightColor)}a.dragging=false},beforeInvalidDrop:function(b,a,c){return true},handleMouseDown:function(b){if(this.dragging){return}var a=this.getDragData(b);if(a&&this.onBeforeDrag(a,b)!==false){this.dragData=a;this.proxy.stop();Ext.dd.DDProxy.prototype.handleMouseDown.apply(this,arguments)}},onBeforeDrag:function(a,b){return true},onStartDrag:Ext.emptyFn,alignElWithMouse:function(){this.proxy.ensureAttachedToBody(true);return Ext.dd.DDProxy.prototype.alignElWithMouse.apply(this,arguments)},startDrag:function(a,b){this.proxy.reset();this.proxy.hidden=false;this.dragging=true;this.proxy.update("");this.onInitDrag(a,b);this.proxy.show()},onInitDrag:function(a,c){var b=this.el.dom.cloneNode(true);b.id=Ext.id();this.proxy.update(b);this.onStartDrag(a,c);return true},getProxy:function(){return this.proxy},hideProxy:function(){this.proxy.hide();this.proxy.reset(true);this.dragging=false},triggerCacheRefresh:function(){Ext.dd.DDM.refreshCache(this.groups)},b4EndDrag:function(a){},endDrag:function(a){this.onEndDrag(this.dragData,a)},onEndDrag:function(a,b){},autoOffset:function(a,b){this.setDelta(-12,-20)},destroy:function(){Ext.dd.DDProxy.prototype.destroy.call(this);Ext.destroy(this.proxy)}},1,0,0,0,0,0,[Ext.dd,"DragSource"],0));(Ext.cmd.derive("Ext.panel.Proxy",Ext.Base,{alternateClassName:"Ext.dd.PanelProxy",moveOnDrag:true,constructor:function(a,b){var c=this;c.panel=a;c.id=c.panel.id+"-ddproxy";Ext.apply(c,b)},insertProxy:true,setStatus:Ext.emptyFn,reset:Ext.emptyFn,update:Ext.emptyFn,stop:Ext.emptyFn,sync:Ext.emptyFn,getEl:function(){return this.ghost.el},getGhost:function(){return this.ghost},getProxy:function(){return this.proxy},hide:function(){var a=this;if(a.ghost){if(a.proxy){a.proxy.destroy();delete a.proxy}a.panel.unghost(null,a.moveOnDrag);delete a.ghost}},show:function(){var b=this,a;if(!b.ghost){a=b.panel.getSize();b.panel.el.setVisibilityMode(Ext.Element.DISPLAY);b.ghost=b.panel.ghost();if(b.insertProxy){b.proxy=b.panel.el.insertSibling({role:"presentation",cls:"x-panel-dd-spacer"});b.proxy.setSize(a)}}},repair:function(b,c,a){this.hide();Ext.callback(c,a||this)},moveProxy:function(a,b){if(this.proxy){a.insertBefore(this.proxy.dom,b)}}},1,0,0,0,0,0,[Ext.panel,"Proxy",Ext.dd,"PanelProxy"],0));(Ext.cmd.derive("Ext.panel.DD",Ext.dd.DragSource,{constructor:function(b,a){var c=this;c.panel=b;c.dragData={panel:b};c.panelProxy=new Ext.panel.Proxy(b,a);c.proxy=c.panelProxy.proxy;Ext.dd.DragSource.prototype.constructor.call(this,b.el,a);c.setupEl(b)},setupEl:function(a){var c=this,d=a.header,b=a.body;if(d){c.setHandleElId(d.id);b=d.el}if(b){b.setStyle("cursor","move");c.scroll=false}else{a.on("boxready",c.setupEl,c,{single:true})}},showFrame:Ext.emptyFn,startDrag:Ext.emptyFn,b4StartDrag:function(a,b){this.panelProxy.show()},b4MouseDown:function(c){var b=c.getXY(),a=b[0],d=b[1];this.autoOffset(a,d)},onInitDrag:function(a,b){this.onStartDrag(a,b);return true},createFrame:Ext.emptyFn,getDragEl:function(b){var a=this.panelProxy.ghost;if(a){return a.el.dom}},endDrag:function(a){this.panelProxy.hide();this.panel.saveState()},autoOffset:function(a,b){a-=this.startPageX;b-=this.startPageY;this.setDelta(a,b)},onInvalidDrop:function(c,b,d){var a=this;if(a.beforeInvalidDrop(c,b,d)!==false){if(a.cachedTarget){if(a.cachedTarget.isNotifyTarget){a.cachedTarget.notifyOut(a,b,a.dragData)}a.cacheTarget=null}if(a.afterInvalidDrop){a.afterInvalidDrop(b,d)}}}},1,0,0,0,0,0,[Ext.panel,"DD"],0));(Ext.cmd.derive("Ext.layout.component.Dock",Ext.layout.component.Component,{alternateClassName:"Ext.layout.component.AbstractDock",type:"dock",horzAxisProps:{name:"horz",oppositeName:"vert",dockBegin:"left",dockEnd:"right",horizontal:true,marginBegin:"margin-left",maxSize:"maxWidth",minSize:"minWidth",pos:"x",setSize:"setWidth",shrinkWrapDock:"shrinkWrapDockWidth",size:"width",sizeModel:"widthModel"},vertAxisProps:{name:"vert",oppositeName:"horz",dockBegin:"top",dockEnd:"bottom",horizontal:false,marginBegin:"margin-top",maxSize:"maxHeight",minSize:"minHeight",pos:"y",setSize:"setHeight",shrinkWrapDock:"shrinkWrapDockHeight",size:"height",sizeModel:"heightModel"},initializedBorders:-1,horizontalCollapsePolicy:{width:true,x:true},verticalCollapsePolicy:{height:true,y:true},finishRender:function(){var b=this,c,a;Ext.layout.component.Component.prototype.finishRender.call(this);c=b.getRenderTarget();a=b.getDockedItems();b.finishRenderItems(c,a)},isItemBoxParent:function(a){return true},isItemShrinkWrap:function(a){return true},noBorderClasses:["x-docked-noborder-top","x-docked-noborder-right","x-docked-noborder-bottom","x-docked-noborder-left"],noBorderClassesSides:{top:"x-docked-noborder-top",right:"x-docked-noborder-right",bottom:"x-docked-noborder-bottom",left:"x-docked-noborder-left"},borderWidthProps:{top:"border-top-width",right:"border-right-width",bottom:"border-bottom-width",left:"border-left-width"},_itemCls:"x-docked",handleItemBorders:function(){var m=this,a=m.owner,l,q,h=m.lastDockedItems,g=m.borders,b=a.dockedItems.generation,c=m.noBorderClassesSides,n=m.borderWidthProps,e,k,p,o,j,d=m.collapsed;if(m.initializedBorders===b||(a.border&&!a.manageBodyBorders)||(a.collapsed&&a.collapseMode==="mini")){return}m.initializedBorders=b;m.collapsed=false;m.lastDockedItems=q=m.getLayoutItems();m.collapsed=d;l={top:[],right:[],bottom:[],left:[]};for(e=0,k=q.length;ed){h=r.constrainedMax;n=d}else{if(jd){g=r.constrainedMax;m=d}else{if(j',addDocked:function(g,k){var j=this,b=j.rendered,c=0,l=j.dockedItems,d=l.getCount(),e,h,m,a;g=j.prepareItems(g);a=g.length;if(b){Ext.suspendLayouts()}if(k===undefined){k=d}else{k=Math.min(k,d)}for(;c','
    ',"{headingText}","
    ","","{% this.renderDockedItems(out,values,0); %}",'
    {bodyCls}',' {baseCls}-body-{ui}',' {parent.baseCls}-body-{parent.ui}-{.}','{childElCls}"','',' {$}="{.}"',"",' role="presentation"',"",' style="{bodyStyle}">',"{%this.renderContainer(out,values);%}","
    ","{% this.renderDockedItems(out,values,1); %}"],headerPosition:"top",iconAlign:"left",titleAlign:"left",titleRotation:"default",beforeRenderConfig:{glyph:null,headerPosition:null,icon:null,iconAlign:null,iconCls:null,title:null,titleAlign:null,titleRotation:null},animCollapse:Ext.enableFx,border:true,closable:false,closeAction:"destroy",closeToolText:"Close panel",collapsed:false,collapsedCls:"collapsed",collapseFirst:true,collapsible:undefined,collapseToolText:"Collapse panel",expandToolText:"Expand panel",constrain:false,constrainHeader:false,dockedItems:null,tbar:null,bbar:null,fbar:null,lbar:null,rbar:null,buttons:null,floatable:true,frame:false,frameHeader:true,hideCollapseTool:false,manageHeight:true,maskElement:"el",minButtonWidth:75,preventHeader:false,shrinkWrapDock:false,titleCollapse:undefined,baseCls:"x-panel",bodyPosProps:{x:"x",y:"y"},componentLayout:"dock",contentPaddingProperty:"bodyPadding",emptyArray:[],isPanel:true,defaultBindProperty:"title",addBodyCls:function(b){var c=this,a=c.rendered?c.body:c.getProtoBody();a.addCls(b);return c},addTool:function(e){if(!Ext.isArray(e)){e=[e]}var d=this,h=d.header,a=e.length,g=d.tools,c,b;if(!h||!h.isHeader){h=null;if(!g){d.tools=g=[]}}for(c=0;c=g||n[e]>0){if(e>=g){e=0;a=0;b++;for(c=0;c0){n[c]--}}}else{e++}}m.push({rowIdx:b,cellIdx:a});for(c=l.colspan||1;c;--c){n[e]=l.rowspan||1;++e}++a}return m},getRenderTree:function(){var j=this,g=j.getLayoutItems(),o=[],p=Ext.apply({tag:"table",id:j.owner.id+"-table","data-ref":"table",role:"presentation",cls:j.tableCls,cellspacing:0,cellpadding:0,cn:{tag:"tbody",id:j.owner.id+"-tbody","data-ref":"tbody",role:"presentation",cn:o}},j.tableAttrs),c=j.tdAttrs,d,e=g.length,n,l,h,b,a,k,m;m=j.calculateCells(g);for(d=0;d0){--this.disabled}},handleAdd:function(b,a){if(!this.disabled){if(a.is(this.selector)){this.onItemAdd(a.ownerCt,a)}if(a.isQueryable){this.onContainerAdd(a)}}},onItemAdd:function(c,b){var e=this,a=e.items,d=e.addHandler;if(!e.disabled){if(d){d.call(e.scope||b,b)}if(a){a.add(b)}}},onItemRemove:function(c,b){var e=this,a=e.items,d=e.removeHandler;if(!e.disabled){if(d){d.call(e.scope||b,b)}if(a){a.remove(b)}}},onContainerAdd:function(g,b){var k=this,j,h,c=k.handleAdd,a=k.handleRemove,d,e;if(g.isContainer){g.on("add",c,k);g.on("dockedadd",c,k);g.on("remove",a,k);g.on("dockedremove",a,k)}if(b!==true){j=g.query(k.selector);for(d=0,h=j.length;dcontainer");for(d=0,h=j.length;d"+h.xtype+"[rowIndex="+h.rowIndex+"]"),function(i){return i!==h});a=d.length;if(!h.destroying&&!h.destroyed){e.remove(h);if(a===1){d[0].columnWidth=1}else{for(b=0;bb.tolerance){b.triggerStart(g)}else{return}}if(b.fireEvent("mousemove",b,g)===false){b.onMouseUp(g)}else{b.onDrag(g);b.fireEvent("drag",b,g)}},onMouseUp:function(b){var a=this;a.mouseIsDown=false;if(a.mouseIsOut){a.mouseIsOut=false;a.onMouseOut(b)}if(a.preventDefault!==false){b.preventDefault()}if(Ext.isIE&&document.releaseCapture){document.releaseCapture()}a.fireEvent("mouseup",a,b);a.endDrag(b)},endDrag:function(c){var b=this,a=b.active;Ext.getDoc().un({mousemove:b.onMouseMove,mouseup:b.onMouseUp,selectstart:b.stopSelect,capture:true,scope:b});b.clearStart();b.active=false;if(a){b.dragEnded=true;b.onEnd(c);b.fireEvent("dragend",b,c)}b._constrainRegion=null},triggerStart:function(b){var a=this;a.clearStart();a.active=true;a.onStart(b);a.fireEvent("dragstart",a,b)},clearStart:function(){var a=this.timer;if(a){clearTimeout(a);this.timer=null}},stopSelect:function(a){a.stopEvent();return false},onBeforeStart:function(a){},onStart:function(a){},onDrag:function(a){},onEnd:function(a){},getDragTarget:function(){return this.dragTarget},getDragCt:function(){return this.el},getConstrainRegion:function(){var a=this;if(a.constrainTo){if(a.constrainTo instanceof Ext.util.Region){return a.constrainTo}if(!a._constrainRegion){a._constrainRegion=Ext.fly(a.constrainTo).getViewRegion()}}else{if(!a._constrainRegion){a._constrainRegion=a.getDragCt().getViewRegion()}}return a._constrainRegion},getXY:function(a){return a?this.constrainModes[a](this,this.lastXY):this.lastXY},getOffset:function(c){var b=this.getXY(c),a=this.startXY;return[b[0]-a[0],b[1]-a[1]]},onDragStart:function(a){a.stopPropagation()},constrainModes:{point:function(b,d){var c=b.dragRegion,a=b.getConstrainRegion();if(!a){return d}c.x=c.left=c[0]=c.right=d[0];c.y=c.top=c[1]=c.bottom=d[1];c.constrainTo(a);return[c.left,c.top]},dragTarget:function(c,g){var b=c.startXY,e=c.startRegion.copy(),a=c.getConstrainRegion(),d;if(!a){return g}e.translateBy(g[0]-b[0],g[1]-b[1]);if(e.right>a.right){g[0]+=d=(a.right-e.right);e.left+=d}if(e.lefta.bottom){g[1]+=d=(a.bottom-e.bottom);e.top+=d}if(e.top[flex]"),m=l.length,c=j==="vertical",k=0,g=c?"width":"height",d=0,r,s;for(;k1||(p&&(h>p))){if(g){g.orphan=1;g.el.setHeight(0)}l=0;h=1;if(q.length){r=q.length-1;n.syncFirstLast(n.getColumns(q[r].items))}q.push(o={index:q.length,items:[],maxHeight:0})}l+=b;o.items.push(c);c.row=o;m.rowIndex=o.index;if(a){c.el.setHeight(1)}g=c}if(q.length){n.syncFirstLast(n.getColumns(q[q.length-1].items))}},beforeLayoutCycle:function(c){var b=this,a=b.owner.items;if(b.splitterGen!==a.generation){b.syncSplitters();b.splitterGen=a.generation}Ext.layout.container.Column.prototype.beforeLayoutCycle.apply(this,arguments)},finishedLayout:function(e){var b=e.childItems,a=b.length,d,j,c,g,h;Ext.layout.container.Column.prototype.finishedLayout.call(this,e);for(c=0;c0){g=b[c];if(d){if(g.isSplitter){d=false}else{if(e){e=false;a.suspendLayouts()}i=a.add(c+1,h.getSplitterConfig())}}else{if(g.isSplitter){if(e){e=false;a.suspendLayouts()}a.remove(g)}else{d=true}}}while(b.length&&(g=b[0]).isSplitter){if(e){e=false;a.suspendLayouts()}a.remove(g)}if(!e){a.resumeLayouts()}},syncFirstLast:function(e){var g=this,b=g.firstColumnCls,j=g.lastColumnCls,d,a=[b,j],c,k,h;e=Ext.Array.from(e);d=e.length;for(c=0;cm-n)?1:0)));if(!n||!r.extensible){a=z.items.items;for(q=0,u=a.length;q0)?(g-a-c.getPadding("lr"))+"px":"","margin-top":"7px"})}else{o=n.body.getPadding("lr");b.setStyle({"float":"left",clear:"left",margin:"0 7px 0 7px"});b.setWidth(n.body.getWidth()-o);p.panelProxy.moveProxy(n.body.dom.firstChild.firstChild,null)}this.scrollPos=n.body.getScroll();if(k.dragover){n.fireEvent("dragover",j)}}return j.status},isRowExtensible:function(d){var c=this,b=c.dashboard,a=b.getMaxColumns()||1;return Ext.Array.from(b.query(">dashboard-column[rowIndex="+d+"]")).length0){++n}}b=l.createColumn();if(d){b.columnWidth=d.columnWidth=d.columnWidth/2;delete d.width}else{b.columnWidth=1}d=l.insert(n,b);o=0}a.el.dom.style.display="";d.insert(o,a);a.isMoving=false;d.updateLayout();Ext.resumeLayouts(true);if(j.drop){l.fireEvent("drop",i)}}},1,0,0,0,0,0,[Ext.dashboard,"DropZone"],0));(Ext.cmd.derive("Ext.dashboard.Part",Ext.Base,{factoryConfig:{type:"part"},isPart:true,_lastId:0,config:{id:null,dashboard:null,viewTemplate:{collapsed:"{collapsed}",columnIndex:"{columnIndex}",id:"{id}",title:"{title}",height:"{height}"}},viewTemplateOptions:{excludeProperties:{bind:1}},valueRe:/^[{][a-z]*[}]$/i,constructor:function(a){this.initConfig(a)},applyViewTemplate:function(a){return Ext.util.ObjectTemplate.create(a,this.viewTemplateOptions)},displayForm:function(a,b,d,c){d.call(c||this,{})},createView:function(b){var d=this,c=d.getViewTemplate(),a=c.apply(b);a.dashboard=d.getDashboard();a.part=d;a._partConfig=b;return a}},1,0,0,0,["part.part"],[[Ext.mixin.Factoryable.prototype.mixinId||Ext.mixin.Factoryable.$className,Ext.mixin.Factoryable],[Ext.mixin.Identifiable.prototype.mixinId||Ext.mixin.Identifiable.$className,Ext.mixin.Identifiable]],[Ext.dashboard,"Part"],0));(Ext.cmd.derive("Ext.dashboard.Dashboard",Ext.panel.Panel,{isDashboard:true,cls:"x-dashboard",bodyCls:"x-dashboard-body",defaultType:"dashboard-column",scrollable:true,layout:null,stateful:false,idSeed:1,config:{parts:null},renderConfig:{maxColumns:4},initComponent:function(){var a=this;if(!a.layout){a.layout={type:"dashboard"}}Ext.panel.Panel.prototype.initComponent.call(this)},applyParts:function(b,c){if(!c){c=new Ext.util.Collection({decoder:Ext.Factory.part})}var d,a;for(d in b){a=b[d];if(Ext.isString(a)){a={type:a}}a.id=d;a.dashboard=this;c.add(a)}return c},getPart:function(a){var b=this.getParts();return b.getByKey(a)},addNew:function(c,b,e){var d=this,a=d.getPart(c);a.displayForm(null,null,function(g){g.type=c;d.addView(g,b,e)})},addView:function(k,e,i){var h=this,g=h.query("dashboard-column"),c=g.length,d=e||0,j=k.id?k:h.createView(k),a=h.columnWidths,b;if(!c){b=h.add(0,h.createColumn({columnWidth:(Ext.isArray(a)?a[0]:1)}));g=[b];c=1}if(d>=c){d=c-1;i=1}if(!i){b=g[d];if(b){return b.add(j)}}if(i>0){++d}b=h.createColumn();if(a){b.columnWidth=a[d]||(a[d]=1)}if(!b.items){b.items=[]}b.items.push(j);b=h.add(b);return b.items.first()},createColumn:function(a){var b=this.cycleLayout;return Ext.apply({items:[],bubbleEvents:["add","remove","childmove","resize"],listeners:{remove:this.onRemoveItem,expand:b,collapse:b,scope:this}},a)},createView:function(c){var e=this,d=c.type,b=e.getPart(d),a=b.createView(c);if(!a.id){a.id=e.id+"_"+d+(e.idSeed++)}a.bubbleEvents=Ext.Array.from(a.bubbleEvents).concat(["expand","collapse"]);a.stateful=e.stateful;return a},initEvents:function(){Ext.panel.Panel.prototype.initEvents.call(this);this.dd=new Ext.dashboard.DropZone(this,this.dropConfig)},cycleLayout:function(){this.updateLayout()},beforeDestroy:function(){if(this.dd){Ext.destroy(this.dd)}Ext.panel.Panel.prototype.beforeDestroy.call(this)},applyState:function(g){delete g.items;var e=this;Ext.panel.Panel.prototype.applyState.call(this,g);var b=g.columnWidths,a=e.items.items,d=a.length,c,h;if(b){h=b.length;e.columnWidths=[];for(c=0;c'],initComponent:function(){Ext.Component.prototype.initComponent.call(this)},beforeRender:function(){Ext.Component.prototype.beforeRender.call(this);Ext.applyIf(this.renderData,{swfId:this.getSwfId()})},afterRender:function(){var b=this,a=Ext.apply({},b.flashParams),c=Ext.apply({},b.flashVars);Ext.Component.prototype.afterRender.call(this);a=Ext.apply({allowScriptAccess:"always",bgcolor:b.backgroundColor,wmode:b.wmode},a);c=Ext.apply({allowedDomain:document.location.hostname},c);new swfobject.embedSWF(b.url,b.getSwfId(),b.swfWidth,b.swfHeight,b.flashVersion,b.expressInstall?b.statics.EXPRESS_INSTALL_URL:undefined,c,a,b.flashAttributes,b.swfCallback.bind(b))},swfCallback:function(b){var a=this;if(b.success){a.swf=Ext.get(b.ref);a.onSuccess();a.fireEvent("success",a)}else{a.onFailure();a.fireEvent("failure",a)}},getSwfId:function(){return this.swfId||(this.swfId="extswf"+this.getAutoId())},onSuccess:function(){this.swf.setStyle("visibility","inherit")},onFailure:Ext.emptyFn,beforeDestroy:function(){var b=this,a=b.swf;if(a){swfobject.removeSWF(b.getSwfId());Ext.destroy(a);delete b.swf}Ext.Component.prototype.beforeDestroy.call(this)},statics:{EXPRESS_INSTALL_URL:"http://swfobject.googlecode.com/svn/trunk/swfobject/expressInstall.swf"}},0,["flash"],["component","box","flash"],{component:true,box:true,flash:true},["widget.flash"],0,[Ext.flash,"Component",Ext,"FlashComponent"],0));(Ext.cmd.derive("Ext.form.action.Action",Ext.Base,{alternateClassName:"Ext.form.Action",submitEmptyText:true,constructor:function(a){if(a){Ext.apply(this,a)}var b=a.params;if(Ext.isString(b)){this.params=Ext.Object.fromQueryString(b)}},run:Ext.emptyFn,onFailure:function(a){var c=this.form,b=c&&!c.destroying&&!c.destroyed;this.response=a;this.failureType=Ext.form.action.Action.CONNECT_FAILURE;if(b){c.afterAction(this,false)}},processResponse:function(a){this.response=a;if(!a.responseText&&!a.responseXML){return true}return(this.result=this.handleResponse(a))},getUrl:function(){return this.url||this.form.url},getMethod:function(){return(this.method||this.form.method||"POST").toUpperCase()},getParams:function(){return Ext.apply({},this.params,this.form.baseParams)},createCallback:function(){var a=this;return{success:a.onSuccess,failure:a.onFailure,scope:a,timeout:(a.timeout||a.form.timeout)*1000}},statics:{CLIENT_INVALID:"client",SERVER_INVALID:"server",CONNECT_FAILURE:"connect",LOAD_FAILURE:"load"}},1,0,0,0,0,0,[Ext.form.action,"Action",Ext.form,"Action"],0));(Ext.cmd.derive("Ext.form.action.Load",Ext.form.action.Action,{alternateClassName:"Ext.form.Action.Load",type:"load",run:function(){Ext.Ajax.request(Ext.apply(this.createCallback(),{method:this.getMethod(),url:this.getUrl(),headers:this.headers,params:this.getParams()}))},onSuccess:function(b){var a=this.processResponse(b),d=this.form,c=d&&!d.destroying&&!d.destroyed;if(a===true||!a.success||!a.data){this.failureType=Ext.form.action.Action.LOAD_FAILURE;if(c){d.afterAction(this,false)}return}if(c){d.clearInvalid();d.setValues(a.data);d.afterAction(this,true)}},handleResponse:function(c){var a=this.form.reader,b,d;if(a){b=a.read(c);d=b.records&&b.records[0]?b.records[0].data:null;return{success:b.success,data:d}}return Ext.decode(c.responseText)}},0,0,0,0,["formaction.load"],0,[Ext.form.action,"Load",Ext.form.Action,"Load"],0));(Ext.cmd.derive("Ext.form.action.Submit",Ext.form.action.Action,{alternateClassName:"Ext.form.Action.Submit",type:"submit",run:function(){var b=this,a=b.form;if(b.clientValidation===false||a.isValid()){b.doSubmit()}else{b.failureType=Ext.form.action.Action.CLIENT_INVALID;a.afterAction(b,false)}},doSubmit:function(){var d=this,b=Ext.apply(d.createCallback(),{url:d.getUrl(),method:d.getMethod(),headers:d.headers}),c=d.form,e=d.jsonSubmit||c.jsonSubmit,a=e?"jsonData":"params",g;if(c.hasUpload()){g=d.buildForm();b.form=g.formEl;b.isUpload=true}else{b[a]=d.getParams(e)}Ext.Ajax.request(b);if(g){d.cleanup(g)}},cleanup:function(h){var e=h.formEl,d=h.uploadEls,b=h.uploadFields,a=b.length,c,g;for(c=0;c',tabGuardIndex:0},tabGuardPositionAttribute:"data-tabguardposition",privates:{initTabGuards:function(){var e=this,c=e.tabGuardPositionAttribute,d=e.tabGuardBeforeEl,g=e.tabGuardAfterEl,a=e.tabGuardIndex,b;if(!e.rendered||!e.tabGuard){return}b=e.el.findTabbableElements({skipSelf:true});if(b[0]&&b[0].hasAttribute(c)){b.shift()}if(b.length&&b[b.length-1].hasAttribute(c)){b.pop()}if(b.length){d.dom.setAttribute("tabIndex",a);d.on("focusenter",e.onTabGuardFocusEnter,e);g.dom.setAttribute("tabIndex",a);g.on("focusenter",e.onTabGuardFocusEnter,e)}else{d.dom.removeAttribute("tabIndex");d.un("focusenter",e.onTabGuardFocusEnter,e);g.dom.removeAttribute("tabIndex");g.un("focusenter",e.onTabGuardFocusEnter,e)}},onTabGuardFocusEnter:function(g,h){var i=this,b=i.el,j=i.tabGuardPositionAttribute,d=h.getAttribute(j),k=g.relatedTarget,a,c,l;if(!k.hasAttribute(j)&&k!==b.dom&&b.contains(k)){c=d==="before"?false:true}else{c=d==="before"?true:false}a=b.findTabbableElements({skipSelf:true});a.shift();a.pop();l=c?a[0]:a[a.length-1];if(l){l.focus()}}}},0,0,0,0,0,0,[Ext.util,"FocusTrap"],0));(Ext.cmd.derive("Ext.window.Window",Ext.panel.Panel,{alternateClassName:"Ext.Window",baseCls:"x-window",resizable:true,draggable:true,constrain:false,constrainHeader:false,plain:false,minimizable:false,maximizable:false,minHeight:50,minWidth:50,expandOnShow:true,collapsible:false,closable:true,hidden:true,autoRender:true,hideMode:"offsets",floating:true,itemCls:"x-window-item",overlapHeader:true,ignoreHeaderBorderManagement:true,alwaysFramed:true,isRootCfg:{isRoot:true},isWindow:true,ariaRole:"dialog",initComponent:function(){var a=this;a.frame=false;Ext.panel.Panel.prototype.initComponent.call(this);if(a.plain){a.addClsWithUI("plain")}a.addStateEvents(["maximize","restore","resize","dragend"])},getElConfig:function(){var b=this,a;a=Ext.panel.Panel.prototype.getElConfig.call(this);a.tabIndex=-1;return a},getFocusEl:function(){return this.getDefaultFocus()||this.el},getState:function(){var b=this,d=Ext.panel.Panel.prototype.getState.call(this)||{},a=!!b.maximized,c=b.ghostBox,e;d.maximized=a;if(a){e=b.restorePos}else{if(c){e=[c.x,c.y]}else{e=b.getPosition()}}Ext.apply(d,{size:a?b.restoreSize:b.getSize(),pos:e});return d},applyState:function(b){var a=this;if(b){a.maximized=b.maximized;if(a.maximized){a.hasSavedRestore=true;a.restoreSize=b.size;a.restorePos=b.pos}else{Ext.apply(a,{width:b.size.width,height:b.size.height,x:b.pos[0],y:b.pos[1]})}}},onRender:function(b,a){var c=this;Ext.panel.Panel.prototype.onRender.apply(this,arguments);if(c.header){c.header.on({scope:c,click:c.onHeaderClick})}if(c.maximizable){c.header.on({scope:c,dblclick:c.toggleMaximize})}},afterRender:function(){var a=this,c=a.header,b;if(a.maximized){a.maximized=false;a.maximize();if(c){c.removeCls(c.indicateDragCls)}}Ext.panel.Panel.prototype.afterRender.call(this);if(a.closable){b=a.getKeyMap();b.on(27,a.onEsc,a)}else{b=a.keyMap}if(b&&a.hidden){b.disable()}},onEsc:function(a,b){b.stopEvent();this.close()},beforeDestroy:function(){var a=this;if(a.rendered){Ext.un("resize",a.onWindowResize,a);delete a.animateTarget;a.hide();Ext.destroy(a.keyMap)}Ext.panel.Panel.prototype.beforeDestroy.call(this)},addTools:function(){var a=this,b=[];Ext.panel.Panel.prototype.addTools.call(this);if(a.minimizable){b.push({type:"minimize",handler:"minimize",scope:a})}if(a.maximizable){b.push({type:a.maximized?"restore":"maximize",handler:"toggleMaximize",scope:a})}if(b.length){a.addTool(b)}},onShow:function(){var a=this;Ext.panel.Panel.prototype.onShow.apply(this,arguments);if(a.expandOnShow){a.expand(false)}a.syncMonitorWindowResize();if(a.keyMap){a.keyMap.enable()}},doClose:function(){var a=this;if(a.hidden){a.fireEvent("close",a);if(a.closeAction==="destroy"){a.destroy()}}else{a.hide(a.animateTarget,a.doClose,a)}},afterHide:function(){var a=this;a.syncMonitorWindowResize();if(a.keyMap){a.keyMap.disable()}Ext.panel.Panel.prototype.afterHide.apply(this,arguments)},onWindowResize:function(){var b=this,a;if(!b.destroyed){if(b.maximized){b.fitContainer()}else{a=b.getSizeModel();if(a.width.natural||a.height.natural){b.updateLayout()}b.doConstrain()}}},minimize:function(){this.fireEvent("minimize",this);return this},resumeHeaderLayout:function(a){this.header.resumeLayouts(a?this.isRootCfg:null)},afterCollapse:function(){var a=this,c=a.header,b=a.tools;if(c&&a.maximizable){c.suspendLayouts();b.maximize.hide();this.resumeHeaderLayout(true)}if(a.resizer){a.resizer.disable()}Ext.panel.Panel.prototype.afterCollapse.apply(this,arguments)},afterExpand:function(){var a=this,d=a.header,b=a.tools,c;if(d){d.suspendLayouts();if(a.maximizable){b.maximize.show();c=true}this.resumeHeaderLayout(c)}if(a.resizer){a.resizer.enable()}Ext.panel.Panel.prototype.afterExpand.apply(this,arguments)},maximize:function(b){var e=this,i=e.header,g=e.tools,d=e.width,a=e.height,c,h;if(!e.maximized){e.expand(false);if(!e.hasSavedRestore){c=e.restoreSize={width:d?d:null,height:a?a:null};e.restorePos=e.getPosition()}if(i){i.suspendLayouts();if(g.maximize){g.maximize.setType("restore")}if(e.collapseTool){e.collapseTool.hide();h=true}e.resumeHeaderLayout(h)}e.el.disableShadow();if(e.dd){e.dd.disable();if(i){i.removeCls(i.indicateDragCls)}}if(e.resizer){e.resizer.disable()}e.el.addCls("x-window-maximized");e.container.addCls("x-window-maximized-ct");e.syncMonitorWindowResize();e.fitContainer(b=(b||!!e.animateTarget)?{callback:function(){e.maximized=true;e.fireEvent("maximize",e)}}:null);if(!b){e.maximized=true;e.fireEvent("maximize",e)}}return e},restore:function(b){var c=this,d=c.tools,g=c.header,a=c.restoreSize,e;if(c.maximized){c.hasSavedRestore=null;c.removeCls("x-window-maximized");if(g){g.suspendLayouts();if(d.maximize){d.maximize.setType("maximize")}if(c.collapseTool){c.collapseTool.show();e=true}c.resumeHeaderLayout(e)}a.x=c.restorePos[0];a.y=c.restorePos[1];c.setBox(a,b=(b||!!c.animateTarget)?{callback:function(){c.el.enableShadow(null,true);c.maximized=false;c.fireEvent("restore",c)}}:null);c.restorePos=c.restoreSize=null;if(c.dd){c.dd.enable();if(g){g.addCls(g.indicateDragCls)}}if(c.resizer){c.resizer.enable()}c.container.removeCls("x-window-maximized-ct");c.syncMonitorWindowResize();if(!b){c.el.enableShadow(null,true);c.maximized=false;c.fireEvent("restore",c)}}return c},syncMonitorWindowResize:function(){var b=this,c=b._monitoringResize,d=b.monitorResize||b.constrain||b.constrainHeader||b.maximized,a=b.hidden||b.destroying||b.destroyed;if(d&&!a){if(!c){Ext.on("resize",b.onWindowResize,b,{buffer:1});b._monitoringResize=true}}else{if(c){Ext.un("resize",b.onWindowResize,b);b._monitoringResize=false}}},toggleMaximize:function(){return this[this.maximized?"restore":"maximize"]()},createGhost:function(){var a=Ext.panel.Panel.prototype.createGhost.apply(this,arguments);a.xtype="window";a.focusOnToFront=false;return a},getDefaultFocus:function(){var c=this,b,d=c.defaultFocus,a;if(d!==undefined){if(Ext.isNumber(d)){b=c.query("button")[d]}else{if(Ext.isString(d)){a=d;if(Ext.validIdRe.test(a)){b=c.down(Ext.makeIdSelector(a))}if(!b){b=c.down(a)}}else{if(d.focus){b=d}}}}return b},privates:{initDraggable:function(){this.initSimpleDraggable()},onHeaderClick:function(c,b){var a;if(c.el.contains(b.getTarget())){a=this.getDefaultFocus();if(a){a.focus()}}},initResizable:function(){Ext.panel.Panel.prototype.initResizable.apply(this,arguments);if(this.maximized){this.resizer.disable()}}}},0,["window"],["component","box","container","panel","window"],{component:true,box:true,container:true,panel:true,window:true},["widget.window"],[[Ext.util.FocusTrap.prototype.mixinId||Ext.util.FocusTrap.$className,Ext.util.FocusTrap]],[Ext.window,"Window",Ext,"Window"],0));(Ext.cmd.derive("Ext.form.Labelable",Ext.Mixin,{isLabelable:true,mixinConfig:{id:"labelable",on:{beforeRender:"beforeLabelRender",onRender:"onLabelRender"}},config:{childEls:["labelEl","bodyEl","errorEl","errorWrapEl","ariaErrorEl"]},labelableRenderTpl:["{beforeLabelTpl}",'","{afterLabelTpl}",'",'','
    ','","
    ","
    ",{disableFormats:true}],activeErrorsTpl:undefined,htmlActiveErrorsTpl:['','
      ','
    • {.}
    • ',"
    ","
    "],plaintextActiveErrorsTpl:['','\n{.}',""],isFieldLabelable:true,formItemCls:"x-form-item",labelCls:"x-form-item-label",topLabelCls:"x-form-item-label-top",rightLabelCls:"x-form-item-label-right",labelInnerCls:"x-form-item-label-inner",topLabelSideErrorCls:"x-form-item-label-top-side-error",errorMsgCls:"x-form-error-msg",errorWrapCls:"x-form-error-wrap",errorWrapSideCls:"x-form-error-wrap-side",errorWrapUnderCls:"x-form-error-wrap-under",errorWrapUnderSideLabelCls:"x-form-error-wrap-under-side-label",baseBodyCls:"x-form-item-body",invalidIconCls:"x-form-invalid-icon",invalidUnderCls:"x-form-invalid-under",noLabelCls:"x-form-item-no-label",fieldBodyCls:"",invalidCls:"x-form-invalid",fieldLabel:undefined,labelAlign:"left",labelWidth:100,labelPad:5,labelSeparator:":",hideLabel:false,hideEmptyLabel:true,preventMark:false,autoFitErrors:true,msgTarget:"qtip",msgTargets:{qtip:1,title:1,under:1,side:1,none:1},noWrap:true,labelableInsertions:["beforeBodyEl","afterBodyEl","beforeLabelTpl","afterLabelTpl","beforeSubTpl","afterSubTpl","beforeLabelTextTpl","afterLabelTextTpl","labelAttrTpl"],statics:{initTip:function(){var b=this.tip,a,c;if(b){return}a={id:"ext-form-error-tip",ui:"form-invalid"};if(Ext.supports.Touch){a.dismissDelay=0;a.anchor="top";a.showDelay=0;a.listeners={beforeshow:function(){this.minWidth=Ext.fly(this.anchorTarget).getWidth()}}}b=this.tip=Ext.create("Ext.tip.QuickTip",a);c=Ext.apply({},b.tagConfig);c.attribute="errorqtip";b.setTagConfig(c)},destroyTip:function(){this.tip=Ext.destroy(this.tip)}},initLabelable:function(){var a=this,b=a.padding;if(b){a.padding=undefined;a.extraMargins=Ext.Element.parseBox(b)}if(Ext.isIE8){a.restoreDisplay=Ext.Function.createDelayed(a.doRestoreDisplay,0,a)}if(!a.activeErrorsTpl){if(a.msgTarget==="title"){a.activeErrorsTpl=a.plaintextActiveErrorsTpl}else{a.activeErrorsTpl=a.htmlActiveErrorsTpl}}a.addCls([a.formItemCls,a.formItemCls+"-"+a.ui]);a.lastActiveError="";a.enableBubble("errorchange")},trimLabelSeparator:function(){var c=this,d=c.labelSeparator,a=c.fieldLabel||"",b=a.substr(a.length-1);return b===d?a.slice(0,-1):a},getFieldLabel:function(){return this.trimLabelSeparator()},setFieldLabel:function(d){d=d||"";var e=this,g=e.labelSeparator,c=e.labelEl,b=e.errorWrapEl,i=(e.labelAlign!=="top"),a=e.noLabelCls,h=e.errorWrapUnderSideLabelCls;e.fieldLabel=d;if(e.rendered){if(Ext.isEmpty(d)&&e.hideEmptyLabel){e.addCls(a);if(i&&b){b.removeCls(h)}}else{if(g){d=e.trimLabelSeparator()+g}c.dom.firstChild.innerHTML=d;e.removeCls(a);if(i&&b){b.addCls(h)}}e.updateLayout()}},setHideLabel:function(a){var b=this;if(a!==b.hideLabel){b.hideLabel=a;if(b.rendered){b[a?"addCls":"removeCls"](b.noLabelCls);b.updateLayout()}}},setHideEmptyLabel:function(a){var c=this,b;if(a!==c.hideEmptyLabel){c.hideEmptyLabel=a;if(c.rendered&&!c.hideLabel){b=a&&!c.getFieldLabel();c[b?"addCls":"removeCls"](c.noLabelCls);c.updateLayout()}}},getInsertionRenderData:function(d,e){var b=e.length,a,c;while(b--){a=e[b];c=this[a];if(c){if(typeof c!=="string"){if(!c.isTemplate){c=Ext.XTemplate.getTpl(this,a)}c=c.apply(d)}}d[a]=c||""}return d},getLabelableRenderData:function(){var p=this,s=p.labelAlign,e=(s==="top"),m=(s==="right"),j=(p.msgTarget==="side"),g=(p.msgTarget==="under"),r=p.errorMsgCls,h=p.labelPad,o=p.labelWidth,b=p.labelClsExtra||"",i=j?p.errorWrapSideCls:p.errorWrapUnderCls,a="",l="",d=p.hasVisibleLabel(),n=p.autoFitErrors,k=p.defaultBodyWidth,c,q;if(e){b+=" "+p.topLabelCls;if(h){l="padding-bottom:"+h+"px;"}if(j&&!n){b+=" "+p.topLabelSideErrorCls}}else{if(m){b+=" "+p.rightLabelCls}if(h){a+=p.getHorizontalPaddingStyle()+h+"px;"}a+="width:"+(o+(h?h:0))+"px;";l="width:"+o+"px"}if(d){if(!e&&g){i+=" "+p.errorWrapUnderSideLabelCls}}if(k){c="min-width:"+k+"px;max-width:"+k+"px;"}q={id:p.id,inputId:p.getInputId(),labelCls:p.labelCls,labelClsExtra:b,labelStyle:a+(p.labelStyle||""),labelInnerStyle:l,labelInnerCls:p.labelInnerCls,unselectableCls:Ext.Element.unselectableCls,bodyStyle:c,baseBodyCls:p.baseBodyCls,fieldBodyCls:p.fieldBodyCls,extraFieldBodyCls:p.extraFieldBodyCls,errorWrapCls:p.errorWrapCls,errorWrapExtraCls:i,renderError:j||g,invalidMsgCls:j?p.invalidIconCls:g?p.invalidUnderCls:"",errorMsgCls:r,growCls:p.grow?p.growCls:"",tipAnchorTarget:p.id+"-inputEl",errorWrapStyle:(j&&!n)?"visibility:hidden":"display:none",fieldLabel:p.getFieldLabel(),labelSeparator:p.labelSeparator};p.getInsertionRenderData(q,p.labelableInsertions);return q},getHorizontalPaddingStyle:function(){return"padding-right:"},beforeLabelRender:function(){var a=this;a.setFieldDefaults(a.getInherited().fieldDefaults);if(a.ownerLayout){a.addCls("x-"+a.ownerLayout.type+"-form-item")}if(!a.hasVisibleLabel()){a.addCls(a.noLabelCls)}},onLabelRender:function(){var d=this,c={},g=Ext.Element,a=d.errorWrapEl,e,b;if(a){a.setVisibilityMode((d.msgTarget==="side"&&!d.autoFitErrors)?g.VISIBILITY:g.DISPLAY)}if(d.extraMargins){e=d.el.getMargin();for(b in e){if(e.hasOwnProperty(b)){c["margin-"+b]=(e[b]+d.extraMargins[b])+"px"}}d.el.setStyle(c)}},hasVisibleLabel:function(){if(this.hideLabel){return false}return !(this.hideEmptyLabel&&!this.getFieldLabel())},getSubTplMarkup:function(){return""},getInputId:function(){return""},getActiveError:function(){return this.activeError||""},hasActiveError:function(){return !!this.getActiveError()},setActiveError:function(a){this.setActiveErrors(a)},getActiveErrors:function(){return this.activeErrors||[]},setActiveErrors:function(i){var g=this,h=g.errorWrapEl,d=g.msgTarget,c=d==="side",k=d==="qtip",a,b,e,j;i=Ext.Array.from(i);e=g.getTpl("activeErrorsTpl");g.activeErrors=i;b=g.activeError=e.apply({fieldLabel:g.fieldLabel,errors:i,listCls:"x-list-plain"});g.renderActiveError();if(g.rendered){a=g.getActionEl();if(c){g.errorEl.dom.setAttribute("data-errorqtip",b)}else{if(k){a.dom.setAttribute("data-errorqtip",b)}else{if(d==="title"){a.dom.setAttribute("title",b)}}}if(d!=="title"){g.ariaErrorEl.dom.innerHTML=i.join(". ");a.dom.setAttribute("aria-describedby",g.ariaErrorEl.id)}if(c||k){Ext.form.Labelable.initTip()}if(!g.msgTargets[d]){j=Ext.get(d);if(j){j.dom.innerHTML=b}}}if(h){h.setVisible(i.length>0);if(c&&g.autoFitErrors){g.labelEl.addCls(g.topLabelSideErrorCls)}g.updateLayout()}},unsetActiveError:function(){var e=this,b=e.errorWrapEl,c=e.msgTarget,a=e.restoreDisplay,d,g;if(e.hasActiveError()){delete e.activeError;delete e.activeErrors;e.renderActiveError();if(e.rendered){d=e.getActionEl();if(c==="qtip"){d.dom.removeAttribute("data-errorqtip")}else{if(c==="title"){d.dom.removeAttribute("title")}}if(c!=="title"){e.ariaErrorEl.dom.innerHTML="";d.dom.removeAttribute("aria-describedby")}if(!e.msgTargets[c]){g=Ext.get(c);if(g){g.dom.innerHTML=""}}if(b){b.hide();if(c==="side"&&e.autoFitErrors){e.labelEl.removeCls(e.topLabelSideErrorCls)}e.updateLayout();if(a){e.el.dom.style.display="block";e.restoreDisplay()}}}}},doRestoreDisplay:function(){var a=this.el;if(a&&a.dom){a.dom.style.display=""}},renderActiveError:function(){var c=this,b=c.getActiveError(),a=!!b;if(b!==c.lastActiveError){c.lastActiveError=b;c.fireEvent("errorchange",c,b)}if(c.rendered&&!c.destroyed&&!c.preventMark){c.toggleInvalidCls(a);if(c.errorEl){c.errorEl.dom.innerHTML=b}}},toggleInvalidCls:function(a){this.el[a?"addCls":"removeCls"](this.invalidCls)},setFieldDefaults:function(b){var a;for(a in b){if(!this.hasOwnProperty(a)){this[a]=b[a]}}}},0,0,0,0,0,0,[Ext.form,"Labelable"],function(){if(Ext.supports.Touch){this.prototype.msgTarget="side"}}));(Ext.cmd.derive("Ext.form.field.Field",Ext.Base,{mixinId:"field",isFormField:true,config:{validation:null,validationField:null},disabled:false,submitValue:true,validateOnChange:true,valuePublishEvent:"change",suspendCheckChange:0,dirty:false,initField:function(){var d=this,c=d.valuePublishEvent,a,b;d.initValue();if(Ext.isString(c)){d.on(c,d.publishValue,d)}else{for(b=0,a=c.length;b name="{name}"',' value="{[Ext.util.Format.htmlEncode(values.value)]}"',' placeholder="{placeholder}"','{%if (values.maxLength !== undefined){%} maxlength="{maxLength}"{%}%}',' readonly="readonly"',' disabled="disabled"',' tabindex="{tabIdx}"',' style="{fieldStyle}"',' {$}="{.}"',' class="{fieldCls} {typeCls} {typeCls}-{ui} {editableCls} {inputCls}" autocomplete="off"/>',{disableFormats:true}],defaultBindProperty:"value",autoEl:{role:"presentation"},subTplInsertions:["inputAttrTpl"],childEls:["inputEl"],inputType:"text",isTextInput:true,invalidText:"The value in this field is invalid",fieldCls:"x-form-field",focusCls:"form-focus",dirtyCls:"x-form-dirty",checkChangeEvents:Ext.isIE&&(!document.documentMode||document.documentMode<=9)?["change","propertychange","keyup"]:["change","input","textInput","keyup","dragdrop"],ignoreChangeRe:/data\-errorqtip|style\.|className/,checkChangeBuffer:50,liquidLayout:true,readOnly:false,readOnlyCls:"x-form-readonly",validateOnBlur:true,hasFocus:false,baseCls:"x-field",fieldBodyCls:"x-field-body",maskOnDisable:false,stretchInputElFixed:true,ariaEl:"inputEl",initComponent:function(){var a=this;Ext.Component.prototype.initComponent.call(this);a.subTplData=a.subTplData||{};a.initLabelable();a.initField();a.initDefaultName();if(a.readOnly){a.addCls(a.readOnlyCls)}a.addCls("x-form-type-"+a.inputType)},initDefaultName:function(){var a=this;if(!a.name){a.name=a.getInputId()}},getInputId:function(){return this.inputId||(this.inputId=this.id+"-inputEl")},getSubTplData:function(c){var e=this,d=e.inputType,a=e.getInputId(),g,b;g=Ext.apply({ui:e.ui,id:a,cmpId:e.id,name:e.name||a,disabled:e.disabled,readOnly:e.readOnly,value:e.getRawValue(),type:d,fieldCls:e.fieldCls,fieldStyle:e.getFieldStyle(),childElCls:c.childElCls,tabIdx:e.tabIndex,inputCls:e.inputCls,typeCls:"x-form-"+(e.isTextInput?"text":d)},e.subTplData);if(e.ariaRole){b={role:e.ariaRole,"aria-hidden":!!e.hidden,"aria-disabled":!!e.disabled,"aria-readonly":!!e.readOnly,"aria-invalid":false};if(e.ariaLabel){b["aria-label"]=e.ariaLabel}if(e.format&&e.formatText&&!g.title){b.title=Ext.String.formatEncode(e.formatText,e.format)}g.inputElAriaAttributes=Ext.apply(b,e.getAriaAttributes())}e.getInsertionRenderData(g,e.subTplInsertions);return g},getSubTplMarkup:function(b){var c=this,d=c.getSubTplData(b),e=c.getTpl("preSubTpl"),g=c.getTpl("postSubTpl"),a="";if(e){a+=e.apply(d)}a+=c.getTpl("fieldSubTpl").apply(d);if(g){a+=g.apply(d)}return a},initRenderData:function(){return Ext.applyIf(Ext.Component.prototype.initRenderData.call(this),this.getLabelableRenderData())},setFieldStyle:function(a){var b=this,c=b.inputEl;if(c){c.applyStyles(a)}b.fieldStyle=a},getFieldStyle:function(){var a=this.fieldStyle;return Ext.isObject(a)?Ext.DomHelper.generateStyles(a,null,true):a||""},onRender:function(){this.callParent(arguments);this.mixins.labelable.self.initTip();this.renderActiveError()},onFocusLeave:function(a){Ext.Component.prototype.onFocusLeave.call(this,a);this.completeEdit()},completeEdit:Ext.emptyFn,isFileUpload:function(){return this.inputType==="file"},getSubmitData:function(){var a=this,b=null,c;if(!a.disabled&&a.submitValue){c=a.getSubmitValue();if(c!==null){b={};b[a.getName()]=c}}return b},getSubmitValue:function(){return this.processRawValue(this.getRawValue())},getRawValue:function(){var b=this,a=(b.inputEl?b.inputEl.getValue():Ext.valueFrom(b.rawValue,""));b.rawValue=a;return a},setRawValue:function(c){var a=this,b=a.rawValue;if(!a.transformRawValue.$nullFn){c=a.transformRawValue(c)}c=Ext.valueFrom(c,"");if(b===undefined||b!==c||a.valueContainsPlaceholder){a.rawValue=c;if(a.inputEl){a.bindChangeEvents(false);a.inputEl.dom.value=c;a.bindChangeEvents(true)}if(a.rendered&&a.reference){a.publishState("rawValue",c)}}return c},transformRawValue:Ext.identityFn,valueToRaw:function(a){return""+Ext.valueFrom(a,"")},rawToValue:Ext.identityFn,processRawValue:Ext.identityFn,getValue:function(){var a=this,b=a.rawToValue(a.processRawValue(a.getRawValue()));a.value=b;return b},setValue:function(b){var a=this;a.setRawValue(a.valueToRaw(b));return a.mixins.field.setValue.call(a,b)},onBoxReady:function(){var a=this;Ext.Component.prototype.onBoxReady.apply(this,arguments);if(a.setReadOnlyOnBoxReady){a.setReadOnly(a.readOnly)}},onDisable:function(){var a=this,b=a.inputEl;Ext.Component.prototype.onDisable.call(this);if(b){b.dom.disabled=true;if(a.hasActiveError()){a.clearInvalid();a.hadErrorOnDisable=true}}if(a.wasValid===false){a.checkValidityChange(true)}},onEnable:function(){var b=this,c=b.inputEl,d=b.preventMark,a;Ext.Component.prototype.onEnable.call(this);if(c){c.dom.disabled=false}if(b.wasValid!==undefined){b.forceValidation=true;b.preventMark=!b.hadErrorOnDisable;a=b.isValid();b.forceValidation=false;b.preventMark=d;b.checkValidityChange(a)}delete b.hadErrorOnDisable},setReadOnly:function(d){var b=this,c=b.inputEl,a=b.readOnly;d=!!d;b[d?"addCls":"removeCls"](b.readOnlyCls);b.readOnly=d;if(c){c.dom.readOnly=d;b.ariaEl.dom.setAttribute("aria-readonly",d)}else{if(b.rendering){b.setReadOnlyOnBoxReady=true}}if(d!==a){b.fireEvent("writeablechange",b,d)}},fireKey:function(a){if(a.isSpecialKey()){this.fireEvent("specialkey",this,a)}},initEvents:function(){var e=this,h=e.inputEl,g=e.onFieldMutation,c=e.checkChangeEvents,a=c.length,b,d;if(h){e.mon(h,Ext.supports.SpecialKeyDownRepeat?"keydown":"keypress",e.fireKey,e);for(b=0;b style="{triggerStyle}">',"{[values.$trigger.renderBody(values)]}",""],statics:{weightComparator:function(a,b){return a.weight-b.weight}},constructor:function(b){var c=this,a;Ext.apply(c,b);if(c.compat4Mode){a=c.cls;c.focusCls=[c.focusCls,a+"-focus"];c.overCls=[c.overCls,a+"-over"];c.clickCls=[c.clickCls,a+"-click"]}},afterFieldRender:function(){this.initEvents()},destroy:function(){var a=this;a.clickRepeater=a.el=Ext.destroy(a.clickRepeater,a.el);a.callParent()},getBodyRenderData:Ext.emptyFn,getEl:function(){return this.el||null},getStateEl:function(){return this.el},hide:function(){var b=this,a=b.el;b.hidden=true;if(a){a.hide()}},initEvents:function(){var d=this,a=d.isFieldEnabled,c=d.getStateEl(),b=d.el;c.addClsOnOver(d.overCls,a,d);c.addClsOnClick(d.clickCls,a,d);if(d.repeatClick){d.clickRepeater=new Ext.util.ClickRepeater(b,{preventDefault:true,handler:d.onClick,listeners:{mousedown:d.onClickRepeaterMouseDown,scope:d},scope:d})}else{d.field.mon(b,{click:d.onClick,mousedown:d.onMouseDown,scope:d})}},isFieldEnabled:function(){return !this.field.disabled},isVisible:function(){var a=this,c=a.field,b=false;if(a.hidden||!c||!a.rendered||a.destroyed){b=true}return !b},onClick:function(){var c=this,a=arguments,g=c.clickRepeater?a[1]:a[0],b=c.handler,d=c.field;if(b&&!d.readOnly&&c.isFieldEnabled()){Ext.callback(c.handler,c.scope,[d,c,g],0,d)}},resolveListenerScope:function(a){return this.field.resolveSatelliteListenerScope(this,a)},onMouseDown:function(a){if(a.pointerType!=="touch"&&!this.field.owns(Ext.Element.getActiveElement())){this.field.inputEl.focus()}if(this.preventMouseDown){a.preventDefault()}},onClickRepeaterMouseDown:function(b,a){if(!a.parentEvent||a.parentEvent.type==="mousedown"){this.field.inputEl.focus()}a.preventDefault()},onFieldBlur:function(){this.getStateEl().removeCls(this.focusCls)},onFieldFocus:function(){this.getStateEl().addCls(this.focusCls)},onFieldRender:function(){var b=this,a=b.el=b.field.triggerWrap.selectNode("#"+b.domId,false);a.setVisibilityMode(Ext.Element.DISPLAY);b.rendered=true},renderBody:function(b){var a=this,c=a.bodyTpl;Ext.apply(b,a.getBodyRenderData());return c?Ext.XTemplate.getTpl(a,"bodyTpl").apply(b):""},renderTrigger:function(a){var c=this,b=c.width,d=c.hidden?"display:none;":"";if(b){d+="width:"+b}return Ext.XTemplate.getTpl(c,"renderTpl").apply({$trigger:c,fieldData:a,ui:a.ui,childElCls:a.childElCls,triggerId:c.domId=c.field.id+"-trigger-"+c.id,cls:c.cls,triggerStyle:d,extraCls:c.extraCls,baseCls:c.baseCls})},setHidden:function(a){if(a!==this.hidden){this[a?"hide":"show"]()}},setVisible:function(a){this.setHidden(!a)},show:function(){var b=this,a=b.el;b.hidden=false;if(a){a.show()}}},1,0,0,0,["trigger.trigger"],[[Ext.mixin.Factoryable.prototype.mixinId||Ext.mixin.Factoryable.$className,Ext.mixin.Factoryable]],[Ext.form.trigger,"Trigger"],0));(Ext.cmd.derive("Ext.form.field.Text",Ext.form.field.Base,{alternateClassName:["Ext.form.TextField","Ext.form.Text"],config:{hideTrigger:false,triggers:undefined},renderConfig:{editable:true},growMin:30,growMax:800,growAppend:"W",allowBlank:true,validateBlank:false,allowOnlyWhitespace:true,minLength:0,maxLength:Number.MAX_VALUE,minLengthText:"The minimum length for this field is {0}",maxLengthText:"The maximum length for this field is {0}",blankText:"This field is required",regexText:"",emptyText:"",emptyCls:"x-form-empty-field",requiredCls:"x-form-required-field",valueContainsPlaceholder:false,ariaRole:"textbox",repeatTriggerClick:false,triggerWrapCls:"x-form-trigger-wrap",triggerWrapFocusCls:"x-form-trigger-wrap-focus",triggerWrapInvalidCls:"x-form-trigger-wrap-invalid",fieldBodyCls:"x-form-text-field-body",inputWrapCls:"x-form-text-wrap",inputWrapFocusCls:"x-form-text-wrap-focus",inputWrapInvalidCls:"x-form-text-wrap-invalid",growCls:"x-form-text-grow",needArrowKeys:true,squashMouseUp:{mouseup:function(){},translate:false,single:true,preventDefault:true},childEls:["triggerWrap","inputWrap"],preSubTpl:['"],initComponent:function(){var b=this,a=b.emptyCls;if(b.allowOnlyWhitespace===false){b.allowBlank=false}if(b.size){b.defaultBodyWidth=b.size*6.5+20}if(!b.onTrigger1Click){b.onTrigger1Click=b.onTriggerClick}Ext.form.field.Base.prototype.initComponent.call(this);if(b.readOnly){b.setReadOnly(b.readOnly)}b.fieldFocusCls=b.baseCls+"-focus";b.emptyUICls=a+" "+a+"-"+b.ui;b.addStateEvents("change")},initEvents:function(){var b=this,a=b.inputEl;Ext.form.field.Base.prototype.initEvents.call(this);if(b.selectOnFocus||b.emptyText){b.mon(a,"mousedown",b.onMouseDown,b)}if(b.maskRe||(b.vtype&&b.disableKeyFilter!==true&&(b.maskRe=Ext.form.field.VTypes[b.vtype+"Mask"]))){b.mon(a,"keypress",b.filterKeys,b)}if(b.enableKeyEvents){b.mon(a,{scope:b,keyup:b.onKeyUp,keydown:b.onKeyDown,keypress:b.onKeyPress})}},isEqual:function(b,a){return this.isEqualAsString(b,a)},onChange:function(b,a){Ext.form.field.Base.prototype.onChange.apply(this,arguments);this.autoSize()},getSubTplData:function(c){var d=this,g=d.getRawValue(),i=d.emptyText&&g.length<1,a=d.maxLength,h,e,b;if(d.enforceMaxLength){if(a===Number.MAX_VALUE){a=undefined}}else{a=undefined}if(i){if(Ext.supports.Placeholder){h=d.emptyText}else{g=d.emptyText;d.valueContainsPlaceholder=true}}e=Ext.apply(Ext.form.field.Base.prototype.getSubTplData.call(this,c),{triggerWrapCls:d.triggerWrapCls,inputWrapCls:d.inputWrapCls,triggers:d.orderedTriggers,maxLength:a,readOnly:!d.editable||d.readOnly,placeholder:h,value:g,fieldCls:d.fieldCls+((i&&(h||g))?" "+d.emptyUICls:"")+(d.allowBlank?"":" "+d.requiredCls)});b=e.inputElAriaAttributes;if(b){b["aria-required"]=!d.allowBlank}return e},onRender:function(){var c=this,b=c.getTriggers(),d=[],e,a;if(Ext.supports.FixedTableWidthBug){c.el._needsTableWidthFix=true}(arguments.callee.$previous||Ext.form.field.Base.prototype.onRender).call(this);if(b){this.invokeTriggers("onFieldRender");for(e in b){d.push(b[e].el)}a=c.triggerEl=c.triggerCell=new Ext.CompositeElement(d,true)}c.inputCell=c.inputWrap},afterRender:function(){var a=this;a.autoSize();Ext.form.field.Base.prototype.afterRender.call(this);a.invokeTriggers("afterFieldRender")},onMouseDown:function(){if(!this.hasFocus){Ext.getDoc().on(this.squashMouseUp)}},applyTriggers:function(h){var j=this,c=j.getHideTrigger(),l=j.readOnly,m=j.orderedTriggers=[],k=j.repeatTriggerClick,b,g,d,a,e;if(!h){h={};if(j.triggerCls&&!j.trigger1Cls){j.trigger1Cls=j.triggerCls}for(e=1;a=j["trigger"+e+"Cls"];e++){h["trigger"+e]={cls:a,extraCls:"x-trigger-index-"+e,handler:"onTrigger"+e+"Click",compat4Mode:true,scope:j}}}for(b in h){if(h.hasOwnProperty(b)){g=h[b];g.field=j;g.id=b;if((l&&g.hideOnReadOnly!==false)||(c&&g.hidden!==false)){g.hidden=true}if(k&&(g.repeatClick!==false)){g.repeatClick=true}d=h[b]=Ext.form.trigger.Trigger.create(g);m.push(d)}}Ext.Array.sort(m,Ext.form.trigger.Trigger.weightComparator);return h},invokeTriggers:function(a,c){var e=this,d=e.getTriggers(),g,b;if(d){for(g in d){if(d.hasOwnProperty(g)){b=d[g];b[a].apply(b,c||[])}}}},getTrigger:function(a){return this.getTriggers()[a]},updateHideTrigger:function(a){this.invokeTriggers(a?"hide":"show")},updateEditable:function(a,b){this.setReadOnlyAttr(!a||this.readOnly)},setReadOnly:function(g){var d=this,c=d.getTriggers(),b=d.getHideTrigger(),a,e;g=!!g;Ext.form.field.Base.prototype.setReadOnly.call(this,g);if(d.rendered){d.setReadOnlyAttr(g||!d.editable)}if(c){for(e in c){a=c[e];if(a.hideOnReadOnly===true||(a.hideOnReadOnly!==false&&!b)){a.setVisible(!g)}}}},setReadOnlyAttr:function(d){var b=this,a="readonly",c=b.inputEl.dom;if(d){c.setAttribute(a,a)}else{c.removeAttribute(a)}if(b.ariaRole){b.ariaEl.dom.setAttribute("aria-readonly",!!d)}},processRawValue:function(c){var b=this,e=b.stripCharsRe,a,d;if(e){if(!e.global){a="g";a+=(e.ignoreCase)?"i":"";a+=(e.multiline)?"m":"";e=new RegExp(e.source,a)}d=c.replace(e,"");if(d!==c){b.setRawValue(d);c=d}}return c},onDisable:function(){Ext.form.field.Base.prototype.onDisable.call(this);if(Ext.isIE){this.inputEl.dom.unselectable="on"}},onEnable:function(){Ext.form.field.Base.prototype.onEnable.call(this);if(Ext.isIE){this.inputEl.dom.unselectable=""}},onKeyDown:function(a){this.fireEvent("keydown",this,a)},onKeyUp:function(a){this.fireEvent("keyup",this,a)},onKeyPress:function(a){this.fireEvent("keypress",this,a)},reset:function(){Ext.form.field.Base.prototype.reset.call(this);this.applyEmptyText()},applyEmptyText:function(){var b=this,a=b.emptyText,c;if(b.rendered&&a){c=b.getRawValue().length<1&&!b.hasFocus;if(Ext.supports.Placeholder){b.inputEl.dom.placeholder=a}else{if(c){b.setRawValue(a);b.valueContainsPlaceholder=true}}if(c){b.inputEl.addCls(b.emptyUICls)}else{b.inputEl.removeCls(b.emptyUICls)}b.autoSize()}},getEmptyText:function(){return this.emptyText},setEmptyText:function(d){var c=this,e=c.inputEl,b=e&&e.dom,a=d||"";if(d){c.emptyText=a;c.applyEmptyText()}else{if(b){if(Ext.supports.Placeholder){b.removeAttribute("placeholder")}else{if(b.value!==c.getRawValue()){b.value="";e.removeCls(c.emptyUICls)}}c.valueContainsPlaceholder=false}}c.emptyText=a;return c},afterFirstLayout:function(){Ext.form.field.Base.prototype.afterFirstLayout.call(this);if(Ext.isIE&&this.disabled){var a=this.inputEl;if(a){a.dom.unselectable="on"}}},toggleInvalidCls:function(a){var b=a?"addCls":"removeCls";Ext.form.field.Base.prototype.toggleInvalidCls.call(this);this.triggerWrap[b](this.triggerWrapInvalidCls);this.inputWrap[b](this.inputWrapInvalidCls)},beforeFocus:function(){var b=this,c=b.inputEl,a=b.emptyText,d;Ext.form.field.Base.prototype.beforeFocus.apply(this,arguments);if((a&&!Ext.supports.Placeholder)&&(c.dom.value===b.emptyText&&b.valueContainsPlaceholder)){b.setRawValue("");d=true;c.removeCls(b.emptyUICls);b.valueContainsPlaceholder=false}else{if(Ext.supports.Placeholder){c.removeCls(b.emptyUICls)}}},onFocus:function(b){var a=this;Ext.form.field.Base.prototype.onFocus.apply(this,arguments);if(a.selectOnFocus){a.inputEl.dom.select()}if(a.emptyText){a.autoSize()}a.addCls(a.fieldFocusCls);a.triggerWrap.addCls(a.triggerWrapFocusCls);a.inputWrap.addCls(a.inputWrapFocusCls);a.invokeTriggers("onFieldFocus",[b])},onBlur:function(b){var a=this;Ext.form.field.Base.prototype.onBlur.apply(this,arguments);a.removeCls(a.fieldFocusCls);a.triggerWrap.removeCls(a.triggerWrapFocusCls);a.inputWrap.removeCls(a.inputWrapFocusCls);a.invokeTriggers("onFieldBlur",[b])},completeEdit:function(a){Ext.form.field.Base.prototype.completeEdit.call(this,a);this.applyEmptyText()},filterKeys:function(b){if((b.ctrlKey&&!b.altKey)||b.isSpecialKey()){return}var a=String.fromCharCode(b.getCharCode());if(!this.maskRe.test(a)){b.stopEvent()}},getState:function(){return this.addPropertyToState(Ext.form.field.Base.prototype.getState.call(this),"value")},applyState:function(a){Ext.form.field.Base.prototype.applyState.apply(this,arguments);if(a.hasOwnProperty("value")){this.setValue(a.value)}},getRawValue:function(){var b=this,a=Ext.form.field.Base.prototype.getRawValue.call(this);if(a===b.emptyText&&b.valueContainsPlaceholder){a=""}return a},setValue:function(b){var a=this,c=a.inputEl;if(c&&a.emptyText&&!Ext.isEmpty(b)){c.removeCls(a.emptyUICls);a.valueContainsPlaceholder=false}Ext.form.field.Base.prototype.setValue.apply(this,arguments);a.applyEmptyText();return a},getErrors:function(l){l=arguments.length?(l==null?"":l):this.processRawValue(this.getRawValue());var g=this,j=Ext.form.field.Base.prototype.getErrors.call(this,l),a=g.validator,d=g.vtype,h=Ext.form.field.VTypes,i=g.regex,k=Ext.String.format,b,e,c;if(Ext.isFunction(a)){b=a.call(g,l);if(b!==true){j.push(b)}}e=g.allowOnlyWhitespace?l:Ext.String.trim(l);if(e.length<1||(l===g.emptyText&&g.valueContainsPlaceholder)){if(!g.allowBlank){j.push(g.blankText)}if(!g.validateBlank){return j}c=true}if(!c&&l.lengthg.maxLength){j.push(k(g.maxLengthText,g.maxLength))}if(d){if(!h[d](l,g)){j.push(g.vtypeText||h[d+"Text"])}}if(i&&!i.test(l)){j.push(g.regexText||g.invalidText)}return j},selectText:function(h,b){var g=this,d=g.getRawValue(),a=d.length,e=g.inputEl.dom,c;if(a>0){h=h===undefined?0:Math.min(h,a);b=b===undefined?a:Math.min(b,a);if(e.setSelectionRange){e.setSelectionRange(h,b)}else{if(e.createTextRange){c=e.createTextRange();c.moveStart("character",h);c.moveEnd("character",b-a);c.select()}}}},getGrowWidth:function(){return this.inputEl.dom.value},autoSize:function(){var c=this,b,h,e,g,a,d;if(c.grow&&c.rendered&&c.getSizeModel().width.auto){g=c.inputEl;b=c.getTriggers();e=0;d=Ext.util.Format.htmlEncode(c.getGrowWidth()||(c.hasFocus?"":c.emptyText)||"");d+=c.growAppend;for(h in b){e+=b[h].el.getWidth()}a=g.getTextWidth(d)+e+c.inputWrap.getBorderWidth("lr")+c.triggerWrap.getBorderWidth("lr");a=Math.min(Math.max(a,c.growMin),c.growMax);c.bodyEl.setWidth(a);c.updateLayout();c.fireEvent("autosize",c,a)}},onDestroy:function(){var a=this;a.invokeTriggers("destroy");Ext.destroy(a.triggerRepeater);Ext.form.field.Base.prototype.onDestroy.call(this)},onTriggerClick:Ext.emptyFn,privates:{getTdType:function(){return"textfield"}},deprecated:{5:{methods:{getTriggerWidth:function(){var b=this.getTriggers(),a=0,c;if(b&&this.rendered){for(c in b){if(b.hasOwnProperty(c)){a+=b[c].el.getWidth()}}}return a}}}}},0,["textfield"],["component","box","field","textfield"],{component:true,box:true,field:true,textfield:true},["widget.textfield"],0,[Ext.form.field,"Text",Ext.form,"TextField",Ext.form,"Text"],0));(Ext.cmd.derive("Ext.form.field.TextArea",Ext.form.field.Text,{alternateClassName:"Ext.form.TextArea",fieldSubTpl:['",{disableFormats:true}],growMin:60,growMax:1000,growAppend:"\n-",enterIsSpecial:false,preventScrollbars:false,returnRe:/\r/g,inputCls:"x-form-textarea",extraFieldBodyCls:"x-form-textarea-body",ariaAttributes:{"aria-multiline":true},getSubTplData:function(b){var d=this,c=d.getFieldStyle(),a=Ext.form.field.Text.prototype.getSubTplData.apply(this,arguments);if(d.grow){if(d.preventScrollbars){a.fieldStyle=(c||"")+";overflow:hidden;height:"+d.growMin+"px"}}return a},afterRender:function(){var a=this;Ext.form.field.Text.prototype.afterRender.apply(this,arguments);a.needsMaxCheck=a.enforceMaxLength&&a.maxLength!==Number.MAX_VALUE&&!Ext.supports.TextAreaMaxLength;if(a.needsMaxCheck){a.inputEl.on("paste",a.onPaste,a)}},transformRawValue:function(a){return this.stripReturns(a)},getValue:function(){return this.stripReturns(Ext.form.field.Text.prototype.getValue.call(this))},valueToRaw:function(a){a=this.stripReturns(a);return Ext.form.field.Text.prototype.valueToRaw.call(this,a)},stripReturns:function(a){if(a&&typeof a==="string"){a=a.replace(this.returnRe,"")}return a},onPaste:function(){var a=this;if(!a.pasteTask){a.pasteTask=new Ext.util.DelayedTask(a.pasteCheck,a)}a.pasteTask.delay(1)},pasteCheck:function(){var b=this,c=b.getValue(),a=b.maxLength;if(c.length>a){c=c.substr(0,a);b.setValue(c)}},fireKey:function(d){var b=this,a=d.getKey(),c;if(d.isSpecialKey()&&(b.enterIsSpecial||(a!==d.ENTER||d.hasModifier()))){b.fireEvent("specialkey",b,d)}if(b.needsMaxCheck&&a!==d.BACKSPACE&&a!==d.DELETE&&!d.isNavKeyPress()&&!b.isCutCopyPasteSelectAll(d,a)){c=b.getValue();if(c.length>=b.maxLength){d.stopEvent()}}},isCutCopyPasteSelectAll:function(b,a){if(b.ctrlKey){return a===b.A||a===b.C||a===b.V||a===b.X}return false},autoSize:function(){var c=this,e,a,b,d;if(c.grow&&c.rendered&&c.getSizeModel().height.auto){e=c.inputEl;b=e.getWidth(true);d=Ext.util.Format.htmlEncode(e.dom.value)||" ";d+=c.growAppend;d=d.replace(/\n/g,"
    ");a=Ext.util.TextMetrics.measure(e,d,b).height+e.getPadding("tb")+c.inputWrap.getBorderWidth("tb")+c.triggerWrap.getBorderWidth("tb");a=Math.min(Math.max(a,c.growMin),c.growMax);c.bodyEl.setHeight(a);c.updateLayout();c.fireEvent("autosize",c,a)}},beforeDestroy:function(){var a=this.pasteTask;if(a){a.cancel();this.pasteTask=null}Ext.form.field.Text.prototype.beforeDestroy.call(this)}},0,["textarea","textareafield"],["component","box","field","textfield","textareafield","textarea"],{component:true,box:true,field:true,textfield:true,textareafield:true,textarea:true},["widget.textarea","widget.textareafield"],0,[Ext.form.field,"TextArea",Ext.form,"TextArea"],0));(Ext.cmd.derive("Ext.window.MessageBox",Ext.window.Window,{OK:1,YES:2,NO:4,CANCEL:8,OKCANCEL:9,YESNO:6,YESNOCANCEL:14,INFO:"x-message-box-info",WARNING:"x-message-box-warning",QUESTION:"x-message-box-question",ERROR:"x-message-box-error",hideMode:"offsets",closeAction:"hide",resizable:false,scrollable:true,title:" ",defaultMinWidth:250,defaultMaxWidth:600,defaultMinHeight:110,defaultMaxHeight:500,minWidth:null,maxWidth:null,minHeight:null,maxHeight:null,constrain:true,cls:["x-message-box","x-hidden-offsets"],layout:{type:"vbox",align:"stretch"},shrinkWrapDock:true,defaultTextHeight:75,minProgressWidth:250,minPromptWidth:250,buttonText:{ok:"OK",yes:"Yes",no:"No",cancel:"Cancel"},buttonIds:["ok","yes","no","cancel"],titleText:{confirm:"Confirm",prompt:"Prompt",wait:"Loading...",alert:"Attention"},baseIconCls:"x-message-box-icon",ariaRole:"alertdialog",makeButton:function(a){var b=this.buttonIds[a];return new Ext.button.Button({handler:this.btnCallback,itemId:b,scope:this,text:this.buttonText[b],minWidth:75})},btnCallback:function(a,c){var b=this,d,e;if(c&&c.type==="keydown"&&!c.isSpecialKey()){c.getTarget(null,null,true).on({keyup:function(g){b.btnCallback(a,g)},single:true});return}if(b.cfg.prompt||b.cfg.multiline){if(b.cfg.multiline){e=b.textArea}else{e=b.textField}d=e.getValue();e.reset()}b.hide();b.userCallback(a.itemId,d,b.cfg)},hide:function(){var b=this,a=b.cfg?b.cfg.cls:"";b.progressBar.reset();if(a){b.removeCls(a)}Ext.window.Window.prototype.hide.apply(this,arguments)},constructor:function(a){var b=this;Ext.window.Window.prototype.constructor.apply(this,arguments);b.minWidth=b.defaultMinWidth=(b.minWidth||b.defaultMinWidth);b.maxWidth=b.defaultMaxWidth=(b.maxWidth||b.defaultMaxWidth);b.minHeight=b.defaultMinHeight=(b.minHeight||b.defaultMinHeight);b.maxHeight=b.defaultMaxHeight=(b.maxHeight||b.defaultMaxHeight)},initComponent:function(a){var e=this,b=e.id,d,c;e.title=e.title||" ";e.iconCls=e.iconCls||"";e.topContainer=new Ext.container.Container({layout:"hbox",padding:10,style:{overflow:"hidden"},items:[e.iconComponent=new Ext.Component({cls:e.baseIconCls}),e.promptContainer=new Ext.container.Container({flex:1,layout:{type:"vbox",align:"stretch"},items:[e.msg=new Ext.Component({id:b+"-msg",cls:e.baseCls+"-text"}),e.textField=new Ext.form.field.Text({id:b+"-textfield",enableKeyEvents:true,listeners:{keydown:e.onPromptKey,scope:e}}),e.textArea=new Ext.form.field.TextArea({id:b+"-textarea",height:75})]})]});e.progressBar=new Ext.ProgressBar({id:b+"-progressbar",margin:"0 10 10 10"});e.items=[e.topContainer,e.progressBar];e.msgButtons=[];for(d=0;d<4;d++){c=e.makeButton(d);e.msgButtons[c.itemId]=c;e.msgButtons.push(c)}e.bottomTb=new Ext.toolbar.Toolbar({id:b+"-toolbar",ui:"footer",dock:"bottom",layout:{pack:"center"},items:[e.msgButtons[0],e.msgButtons[1],e.msgButtons[2],e.msgButtons[3]]});e.dockedItems=[e.bottomTb];e.on("close",e.onClose,e);Ext.window.Window.prototype.initComponent.call(this)},onClose:function(){var a=this.header.child("[type=close]");if(a){a.itemId="cancel";this.btnCallback(a);delete a.itemId}},onPromptKey:function(a,c){var b=this;if(c.keyCode===c.RETURN||c.keyCode===10){if(b.msgButtons.ok.isVisible()){b.msgButtons.ok.handler.call(b,b.msgButtons.ok)}else{if(b.msgButtons.yes.isVisible()){b.msgButtons.yes.handler.call(b,b.msgButtons.yes)}}}},reconfigure:function(c){var t=this,q=0,g=true,u=t.buttonText,e=t.resizer,o=t.header,s=o&&!o.isHeader,d=c&&(c.message||c.msg),v,m,j,p,r,h,b,k,n,l,a;t.updateButtonText();t.cfg=c=c||{};l=c.wait;if(c.width){m=c.width}if(c.height){j=c.height}t.minWidth=c.minWidth||t.defaultMinWidth;t.maxWidth=c.maxWidth||t.defaultMaxWidth;t.minHeight=c.minHeight||t.defaultMinHeight;t.maxHeight=c.maxHeight||t.defaultMaxHeight;if(e){v=e.resizeTracker;e.minWidth=v.minWidth=t.minWidth;e.maxWidth=v.maxWidth=t.maxWidth;e.minHeight=v.minHeight=t.minHeight;e.maxHeight=v.maxHeight=t.maxHeight}delete t.defaultFocus;if(c.defaultFocus){t.defaultFocus=c.defaultFocus}t.animateTarget=c.animateTarget||undefined;t.modal=c.modal!==false;t.setTitle(c.title||(s&&o.title)||t.title);t.setIconCls(c.iconCls||(s&&o.iconCls)||t.iconCls);if(Ext.isObject(c.buttons)){t.buttonText=c.buttons;q=0}else{t.buttonText=c.buttonText||t.buttonText;q=Ext.isNumber(c.buttons)?c.buttons:0}q=q|t.updateButtonText();t.buttonText=u;Ext.suspendLayouts();t.width=t.height=null;if(m||j){if(m){t.setWidth(m)}if(j){t.setHeight(j)}}t.hidden=false;if(!t.rendered){t.render(Ext.getBody())}t.closable=c.closable!==false&&!l;o=t.header;if(o){a=o.child("[type=close]");if(a){a.setVisible(t.closable)}if(!c.title&&!t.closable&&!c.iconCls){o.hide()}else{o.show()}}t.liveDrag=!c.proxyDrag;t.userCallback=Ext.Function.bindCallback(c.callback||c.fn||Ext.emptyFn,c.scope||Ext.global);t.setIcon(c.icon);b=t.msg;if(d){b.setHtml(d);b.show()}else{b.hide()}r=t.textArea;h=t.textField;if(c.prompt||c.multiline){t.multiline=c.multiline;if(c.multiline){r.setValue(c.value);r.setHeight(c.defaultTextHeight||t.defaultTextHeight);r.show();h.hide();t.defaultFocus=r}else{h.setValue(c.value);r.hide();h.show();t.defaultFocus=h}}else{r.hide();h.hide()}k=t.progressBar;if(c.progress||l){k.show();t.updateProgress(0,c.progressText);if(l){k.wait(l===true?c.waitConfig:l)}}else{k.hide()}n=t.msgButtons;for(p=0;p<4;p++){if(q&Math.pow(2,p)){if(!t.defaultFocus){t.defaultFocus=n[p]}n[p].show();g=false}else{n[p].hide()}}if(g){t.bottomTb.hide()}else{t.bottomTb.show()}Ext.resumeLayouts(true)},updateButtonText:function(){var d=this,c=d.buttonText,b=0,e,a;for(e in c){if(c.hasOwnProperty(e)){a=d.msgButtons[e];if(a){if(d.cfg&&d.cfg.buttonText){b=b|Math.pow(2,Ext.Array.indexOf(d.buttonIds,e))}if(a.text!==c[e]){a.setText(c[e])}}}}return b},show:function(a){var c=this,b;a=a||{};if(Ext.Component.layoutSuspendCount){Ext.on({resumelayouts:function(){c.show(a)},single:true});return c}c.reconfigure(a);if(a.cls){c.addCls(a.cls)}b=c.query("textfield:not([hidden]),textarea:not([hidden]),button:not([hidden])");c.preventFocusOnActivate=!b.length;c.hidden=true;Ext.window.Window.prototype.show.call(this);return c},onShow:function(){Ext.window.Window.prototype.onShow.apply(this,arguments);this.center()},updateText:function(a){this.msg.setHtml(a)},setIcon:function(d,c,a){var e=this,g=e.iconComponent,b=e.messageIconCls;if(b){g.removeCls(b)}if(d){g.show();if(c||a){g.setSize(c||g.getWidth(),a||g.getHeight())}g.addCls("x-dlg-icon");g.addCls(e.messageIconCls=d)}else{g.removeCls("x-dlg-icon");g.hide()}return e},updateProgress:function(c,a,b){this.progressBar.updateProgress(c,a);if(b){this.updateText(b)}return this},onEsc:function(){if(this.closable!==false){Ext.window.Window.prototype.onEsc.apply(this,arguments)}},confirm:function(a,d,c,b){if(Ext.isString(a)){a={title:a,icon:this.QUESTION,message:d,buttons:this.YESNO,callback:c,scope:b}}return this.show(a)},prompt:function(g,d,c,b,a,e){if(Ext.isString(g)){g={prompt:true,title:g,minWidth:this.minPromptWidth,message:d,buttons:this.OKCANCEL,callback:c,scope:b,multiline:a,value:e}}return this.show(g)},wait:function(b,c,a){if(Ext.isString(b)){b={title:c,message:b,closable:false,wait:true,modal:true,minWidth:this.minProgressWidth,waitConfig:a}}return this.show(b)},alert:function(d,c,b,a){if(Ext.isString(d)){d={title:d,message:c,buttons:this.OK,fn:b,scope:a,minWidth:this.minWidth}}return this.show(d)},progress:function(c,b,a){if(Ext.isString(c)){c={title:c,message:b,progress:true,progressText:a}}return this.show(c)}},1,["messagebox"],["component","box","container","panel","window","messagebox"],{component:true,box:true,container:true,panel:true,window:true,messagebox:true},["widget.messagebox"],0,[Ext.window,"MessageBox"],function(a){Ext.onInternalReady(function(){Ext.MessageBox=Ext.Msg=new a()})}));(Ext.cmd.derive("Ext.form.Basic",Ext.util.Observable,{alternateClassName:"Ext.form.BasicForm",taskDelay:10,constructor:function(b,c){var d=this,a;d.owner=b;d.fieldMonitors={validitychange:d.checkValidityDelay,enable:d.checkValidityDelay,disable:d.checkValidityDelay,dirtychange:d.checkDirtyDelay,errorchange:d.checkErrorDelay,scope:d};d.checkValidityTask=new Ext.util.DelayedTask(d.checkValidity,d);d.checkDirtyTask=new Ext.util.DelayedTask(d.checkDirty,d);d.checkErrorTask=new Ext.util.DelayedTask(d.checkError,d);d.monitor=new Ext.container.Monitor({selector:"[isFormField]:not([excludeForm])",scope:d,addHandler:d.onFieldAdd,removeHandler:d.onFieldRemove,invalidateHandler:d.onMonitorInvalidate});d.monitor.bind(b);Ext.apply(d,c);if(Ext.isString(d.paramOrder)){d.paramOrder=d.paramOrder.split(/[\s,|]/)}a=d.reader;if(a&&!a.isReader){if(typeof a==="string"){a={type:a}}d.reader=Ext.createByAlias("reader."+a.type,a)}a=d.errorReader;if(a&&!a.isReader){if(typeof a==="string"){a={type:a}}d.errorReader=Ext.createByAlias("reader."+a.type,a)}Ext.util.Observable.prototype.constructor.call(this)},initialize:function(){this.initialized=true;this.onValidityChange(!this.hasInvalidField())},timeout:30,paramsAsHash:false,waitTitle:"Please Wait...",trackResetOnLoad:false,wasDirty:false,destroy:function(){var b=this,a=b.monitor;if(a){a.unbind();b.monitor=null}b.clearListeners();b.checkValidityTask.cancel();b.checkDirtyTask.cancel();b.checkErrorTask.cancel();b.checkValidityTask=b.checkDirtyTask=b.checkErrorTask=null;Ext.util.Observable.prototype.destroy.call(this)},onFieldAdd:function(a){a.on(this.fieldMonitors);this.onMonitorInvalidate()},onFieldRemove:function(a){a.un(this.fieldMonitors);this.onMonitorInvalidate()},onMonitorInvalidate:function(){if(this.initialized){this.checkValidityDelay()}},getFields:function(){return this.monitor.getItems()},getBoundItems:function(){var a=this._boundItems;if(!a||a.getCount()===0){a=this._boundItems=new Ext.util.MixedCollection();a.addAll(this.owner.query("[formBind]"))}return a},hasInvalidField:function(){return !!this.getFields().findBy(function(c){var a=c.preventMark,b;c.preventMark=true;b=c.isValid();c.preventMark=a;return !b})},isValid:function(){var a=this,b;Ext.suspendLayouts();b=a.getFields().filterBy(function(c){return !c.validate()});Ext.resumeLayouts(true);return b.length<1},checkValidity:function(){var b=this,a;if(b.destroyed){return}a=!b.hasInvalidField();if(a!==b.wasValid){b.onValidityChange(a);b.fireEvent("validitychange",b,a);b.wasValid=a}},checkValidityDelay:function(){var a=this.taskDelay;if(a){this.checkValidityTask.delay(a)}else{this.checkValidity()}},checkError:function(){this.fireEvent("errorchange",this)},checkErrorDelay:function(){var a=this.taskDelay;if(a){this.checkErrorTask.delay(a)}else{this.checkError()}},onValidityChange:function(g){var d=this.getBoundItems(),b,c,a,e;if(d){b=d.items;a=b.length;for(c=0;c',' {$}="{.}"',"",' role="presentation"',"",">","{%this.renderContainer(out,values)%}",""],initComponent:function(){var a=this;a.initLabelable();a.initFieldAncestor();Ext.container.Container.prototype.initComponent.call(this);a.initMonitor()},onAdd:function(a){var b=this;if(a.isLabelable&&Ext.isGecko&&Ext.firefoxVersion<37&&b.layout.type==="absolute"&&!b.hideLabel&&b.labelAlign!=="top"){a.x+=(b.labelWidth+b.labelPad)}Ext.container.Container.prototype.onAdd.apply(this,arguments);if(a.isLabelable&&b.combineLabels){a.oldHideLabel=a.hideLabel;a.hideLabel=true}b.updateLabel()},onRemove:function(a,b){var c=this;Ext.container.Container.prototype.onRemove.apply(this,arguments);if(!b){if(a.isLabelable&&c.combineLabels){a.hideLabel=a.oldHideLabel}c.updateLabel()}},initRenderData:function(){var a=this,b=Ext.container.Container.prototype.initRenderData.call(this);b.containerElCls=a.containerElCls;b=Ext.applyIf(b,a.getLabelableRenderData());b.tipAnchorTarget=a.id+"-containerEl";return b},getFieldLabel:function(){var a=this.fieldLabel||"";if(!a&&this.combineLabels){a=Ext.Array.map(this.query("[isFieldLabelable]"),function(b){return b.getFieldLabel()}).join(this.labelConnector)}return a},getSubTplData:function(){var a=this.initRenderData();Ext.apply(a,this.subTplData);return a},getSubTplMarkup:function(b){var d=this,a=d.getTpl("fieldSubTpl"),c;if(!a.renderContent){d.setupRenderTpl(a)}c=a.apply(d.getSubTplData(b));return c},updateLabel:function(){var b=this,a=b.labelEl;if(a){b.setFieldLabel(b.getFieldLabel())}},onFieldErrorChange:function(){if(this.combineErrors){var c=this,d=c.getActiveError(),b=Ext.Array.filter(c.query("[isFormField]"),function(e){return e.hasActiveError()}),a=c.getCombinedErrors(b);if(a){c.setActiveErrors(a)}else{c.unsetActiveError()}if(d!==c.getActiveError()){c.updateLayout()}}},getCombinedErrors:function(e){var k=[],c,l=e.length,i,d,j,b,g,h;for(c=0;c','','',"{% this.renderColumn(out,parent,xindex-1) %}","","",""],lastOwnerItemsGeneration:null,beginLayout:function(b){var k=this,e,d,h,a,j,g=0,m=0,l=k.autoFlex,c=k.innerCt.dom.style;Ext.layout.container.Container.prototype.beginLayout.apply(this,arguments);e=k.columnNodes;b.innerCtContext=b.getEl("innerCt",k);if(!b.widthModel.shrinkWrap){d=e.length;if(k.columnsArray){for(h=0;ha){d=b-a;g=e.rowEl;for(c=0;c','',"{beforeBoxLabelTpl}",'","{afterBoxLabelTpl}","",' tabindex="{tabIdx}"',' disabled="disabled"',' style="{fieldStyle}"',' class="{checkboxCls} {clipCls}" autocomplete="off" hidefocus="true" ',' {$}="{.}"',"/>",'','',"{beforeBoxLabelTpl}",'","{afterBoxLabelTpl}","","",{disableFormats:true,compiled:true}],publishes:{checked:1},subTplInsertions:["beforeBoxLabelTpl","afterBoxLabelTpl","beforeBoxLabelTextTpl","afterBoxLabelTextTpl","boxLabelAttrTpl","inputAttrTpl"],isCheckbox:true,focusCls:"form-checkbox-focus",fieldBodyCls:"x-form-cb-wrap",checked:false,checkedCls:"x-form-cb-checked",boxLabelCls:"x-form-cb-label",boxLabelAlign:"after",afterLabelCls:"x-form-cb-after",wrapInnerCls:"x-form-cb-wrap-inner",noBoxLabelCls:"x-form-cb-no-box-label",inputValue:"on",checkChangeEvents:[],inputType:"checkbox",isTextInput:false,ariaRole:"checkbox",onRe:/^on$/i,inputCls:"x-form-cb",_checkboxCls:"x-form-cb-input",_clipCls:"x-hidden-clip",initComponent:function(){var a=this,b=a.value;if(b!==undefined){a.checked=a.isChecked(b,a.inputValue)}Ext.form.field.Base.prototype.initComponent.call(this);a.getManager().add(a)},initDefaultName:Ext.emptyFn,initValue:function(){var b=this,a=!!b.checked;b.originalValue=b.lastValue=a;b.setValue(a)},getElConfig:function(){var a=this;if(a.isChecked(a.rawValue,a.inputValue)){a.addCls(a.checkedCls)}return Ext.form.field.Base.prototype.getElConfig.call(this)},getSubTplData:function(b){var d=this,c=d.boxLabel,h=d.boxLabelAlign,g=h==="before",e,a;e=Ext.apply(Ext.form.field.Base.prototype.getSubTplData.call(this,b),{clipCls:d._clipCls,checkboxCls:d._checkboxCls,disabled:d.readOnly||d.disabled,wrapInnerCls:d.wrapInnerCls,boxLabel:c,boxLabelCls:d.boxLabelCls,boxLabelAlign:h,labelAlignedBefore:g,afterLabelCls:g?d.afterLabelCls:"",noBoxLabelCls:!c?d.noBoxLabelCls:"",inputName:d.name||d.id});a=e.inputElAriaAttributes;if(a){a["aria-checked"]=!!d.checked;a["aria-labelledby"]=d.id+"-boxLabelEl"}return e},initEvents:function(){var a=this;Ext.form.field.Base.prototype.initEvents.call(this);a.inputEl.on({click:"onBoxClick",scope:a,translate:false});a.displayEl.on({click:"onBoxClick",mousedown:"_onDisplayElMouseDown",scope:a})},setBoxLabel:function(a){var b=this;b.boxLabel=a;if(b.rendered){b.boxLabelEl.setHtml(a);b.boxLabelEl[a?"removeCls":"addCls"](b.noBoxLabelCls);b.updateLayout()}},onBoxClick:function(){var a=this;if(!a.disabled&&!a.readOnly){a.setValue(!a.checked)}},getRawValue:function(){return this.checked},getValue:function(){return this.checked},getSubmitValue:function(){var a=this.uncheckedValue,b=Ext.isDefined(a)?a:null;return this.checked?this.inputValue:b},isChecked:function(b,a){return(b===true||b==="true"||b==="1"||b===1||(((Ext.isString(b)||Ext.isNumber(b))&&a)?b==a:this.onRe.test(b)))},setRawValue:function(c){var b=this,d=b.inputEl,e=b.displayEl,a=b.isChecked(c,b.inputValue);if(d){b[a?"addCls":"removeCls"](b.checkedCls);if(b.ariaRole){b.ariaEl.dom.setAttribute("aria-checked",a)}}if(Ext.isIE8&&e&&a!==b.lastValue){e.repaint()}b.checked=b.rawValue=a;if(!b.duringSetValue){b.lastValue=a}return a},setValue:function(g){var e=this,c,b,a,d;if(Ext.isArray(g)){c=e.getManager().getByName(e.name,e.getFormId()).items;a=c.length;for(b=0;b style="{bodyStyle}">',"{%this.renderContainer(out,values);%}",""],stateEvents:["collapse","expand"],maskOnDisable:false,beforeDestroy:function(){var b=this,a=b.legend;if(a){delete a.ownerCt;a.destroy();b.legend=null}Ext.container.Container.prototype.beforeDestroy.call(this)},initComponent:function(){var b=this,a=b.baseCls;if(b.ariaRole&&!b.ariaLabel){b.ariaLabel=Ext.String.formatEncode(b.descriptionText,b.title||"")}b.ariaRenderAttributes=b.ariaRenderAttributes||{};b.ariaRenderAttributes["aria-expanded"]=!b.collapsed;b.initFieldAncestor();Ext.container.Container.prototype.initComponent.call(this);b.layout.managePadding=b.layout.manageOverflow=false;if(b.collapsed){b.addCls(a+"-collapsed");b.collapse()}if(b.title||b.checkboxToggle||b.collapsible){b.addTitleClasses();b.legend=b.createLegendCt()}b.initMonitor()},initRenderData:function(){var a=this,b=Ext.container.Container.prototype.initRenderData.call(this);b.bodyTargetCls=a.bodyTargetCls;a.protoBody.writeTo(b);delete a.protoBody;return b},getState:function(){var a=Ext.container.Container.prototype.getState.call(this);a=this.addPropertyToState(a,"collapsed");return a},afterCollapse:Ext.emptyFn,afterExpand:Ext.emptyFn,collapsedHorizontal:function(){return true},collapsedVertical:function(){return true},createLegendCt:function(){var c=this,a=[],d={baseCls:c.baseCls+"-header",layout:"container",ui:c.ui,id:c.id+"-legend",autoEl:"legend",ariaRole:null,items:a,ownerCt:c,shrinkWrap:true,ownerLayout:c.componentLayout},b;if(c.checkboxToggle){a.push(c.createCheckboxCmp())}else{if(c.collapsible){a.push(c.createToggleCmp())}}a.push(c.createTitleCmp());b=new Ext.container.Container(d);b.collapseImmune=true;b.getInherited().collapseImmune=true;return b},createTitleCmp:function(){var b=this,a={html:b.title,ui:b.ui,cls:b.baseCls+"-header-text",id:b.id+"-legendTitle",ariaRole:"presentation"};if(b.collapsible&&b.toggleOnTitleClick){a.listeners={click:{element:"el",scope:b,fn:b.toggle}};a.cls+=" "+b.baseCls+"-header-text-collapsible"}b.titleCmp=new Ext.Component(a);return b.titleCmp},createCheckboxCmp:function(){var b=this,d="-checkbox",a=b.baseCls+"-header"+d,c;a+=" "+a+"-"+b.ui;b.checkboxCmp=c=new Ext.form.field.Checkbox({hideEmptyLabel:true,name:b.checkboxName||b.id+d,cls:a,id:b.id+"-legendChk",ui:b.checkboxUI,checked:!b.collapsed,msgTarget:"none",listeners:{change:b.onCheckChange,scope:b},ariaLabel:b.expandText});return c},createToggleCmp:function(){var a=this,b;a.toggleCmp=b=new Ext.panel.Tool({cacheHeight:false,cls:a.baseCls+"-header-tool-"+a.ui,type:"toggle",handler:a.toggle,id:a.id+"-legendToggle",scope:a,ariaRole:"checkbox",ariaLabel:a.expandText,ariaRenderAttributes:{"aria-checked":!a.collapsed}});return b},doRenderLegend:function(b,e){var d=e.$comp,c=d.legend,a;if(c){c.ownerLayout.configureItem(c);a=c.getRenderTree();Ext.DomHelper.generateMarkup(a,b)}},getCollapsed:function(){return this.collapsed?"top":false},getCollapsedDockedItems:function(){var a=this.legend;return a?[a]:[]},setTitle:function(c){var b=this,a=b.legend;b.title=c;b.ariaLabel=Ext.String.formatEncode(b.descriptionText,c||"");if(b.rendered){if(!a){b.legend=a=b.createLegendCt();b.addTitleClasses();a.ownerLayout.configureItem(a);a.render(b.el,0)}b.titleCmp.update(c);b.ariaEl.dom.setAttribute("aria-label",b.ariaLabel)}else{if(a){b.titleCmp.update(c)}else{b.addTitleClasses();b.legend=b.createLegendCt()}}return b},addTitleClasses:function(){var b=this,c=b.title,a=b.baseCls;if(c){b.addCls(a+"-with-title")}if(c||b.checkboxToggle||b.collapsible){b.addCls(a+"-with-legend")}},expand:function(){return this.setExpanded(true)},collapse:function(){return this.setExpanded(false)},setExpanded:function(b){var c=this,d=c.checkboxCmp,e=c.toggleCmp,a=b?"expand":"collapse";if(!c.rendered||c.fireEvent("before"+a,c)!==false){b=!!b;if(d){d.setValue(b)}else{if(e&&e.ariaEl.dom){e.ariaEl.dom.setAttribute("aria-checked",b)}}if(b){c.removeCls(c.baseCls+"-collapsed")}else{c.addCls(c.baseCls+"-collapsed")}if(c.ariaEl.dom){c.ariaEl.dom.setAttribute("aria-expanded",!!b)}c.collapsed=!b;if(b){delete c.getInherited().collapsed}else{c.getInherited().collapsed=true}if(c.rendered){c.updateLayout({isRoot:false});c.fireEvent(a,c)}}return c},getRefItems:function(a){var c=Ext.container.Container.prototype.getRefItems.apply(this,arguments),b=this.legend;if(b){c.unshift(b);if(a){c.unshift.apply(c,b.getRefItems(true))}}return c},toggle:function(){this.setExpanded(!!this.collapsed)},privates:{applyTargetCls:function(a){this.bodyTargetCls=a},finishRender:function(){var a=this.legend;Ext.container.Container.prototype.finishRender.call(this);if(a){a.finishRender()}},getProtoBody:function(){var b=this,a=b.protoBody;if(!a){b.protoBody=a=new Ext.util.ProtoElement({styleProp:"bodyStyle",styleIsText:true})}return a},getDefaultContentTarget:function(){return this.body},getTargetEl:function(){return this.body||this.frameBody||this.el},initPadding:function(e){var c=this,a=c.getProtoBody(),d=c.padding,b;if(d!==undefined){if(Ext.isIE8){d=c.parseBox(d);b=Ext.Element.parseBox(0);b.top=d.top;d.top=0;a.setStyle("padding",c.unitizeBox(b))}e.setStyle("padding",c.unitizeBox(d))}},onCheckChange:function(b,a){this.setExpanded(a)},setupRenderTpl:function(a){Ext.container.Container.prototype.setupRenderTpl.apply(this,arguments);a.renderLegend=this.doRenderLegend}}},0,["fieldset"],["component","box","container","fieldset"],{component:true,box:true,container:true,fieldset:true},["widget.fieldset"],[["fieldAncestor",Ext.form.FieldAncestor]],[Ext.form,"FieldSet"],0));(Ext.cmd.derive("Ext.form.Label",Ext.Component,{autoEl:"label",maskOnDisable:false,getElConfig:function(){var a=this;a.html=a.text?Ext.util.Format.htmlEncode(a.text):(a.html||"");return Ext.apply(Ext.Component.prototype.getElConfig.call(this),{htmlFor:a.forId||""})},setText:function(c,b){var a=this;b=b!==false;if(b){a.text=c;delete a.html}else{a.html=c;delete a.text}if(a.rendered){a.el.dom.innerHTML=b!==false?Ext.util.Format.htmlEncode(c):c;a.updateLayout()}return a}},0,["label"],["component","box","label"],{component:true,box:true,label:true},["widget.label"],0,[Ext.form,"Label"],0));(Ext.cmd.derive("Ext.form.Panel",Ext.panel.Panel,{alternateClassName:["Ext.FormPanel","Ext.form.FormPanel"],layout:"anchor",bodyAriaRole:"form",basicFormConfigs:["api","baseParams","errorReader","jsonSubmit","method","paramOrder","paramsAsHash","reader","standardSubmit","timeout","trackResetOnLoad","url","waitMsgTarget","waitTitle"],initComponent:function(){var a=this;if(a.frame){a.border=false}a.initFieldAncestor();Ext.panel.Panel.prototype.initComponent.call(this);a.relayEvents(a.form,["beforeaction","actionfailed","actioncomplete","validitychange","dirtychange"]);if(a.pollForChanges){a.startPolling(a.pollInterval||500)}},initItems:function(){Ext.panel.Panel.prototype.initItems.call(this);this.initMonitor();this.form=this.createForm()},afterFirstLayout:function(){Ext.panel.Panel.prototype.afterFirstLayout.apply(this,arguments);this.form.initialize()},createForm:function(){var b={},d=this.basicFormConfigs,a=d.length,c=0,e;for(;c150){delete a.lastDownArrow}if(!a.isExpanded){b.stopEvent();a.onTriggerClick();a.lastDownArrow=b.time}else{if(!b.isStopped&&(b.time-a.lastDownArrow)<150){delete a.lastDownArrow}}},expand:function(){var d=this,b,a,c,e;if(d.rendered&&!d.isExpanded&&!d.destroyed){b=d.bodyEl;c=d.getPicker();e=Ext.getDoc();c.setMaxHeight(c.initialConfig.maxHeight);if(d.matchFieldWidth){c.setWidth(d.bodyEl.getWidth())}c.show();d.isExpanded=true;d.alignPicker();b.addCls(d.openCls);if(d.ariaRole){a=d.ariaEl.dom;a.setAttribute("aria-owns",c.listEl?c.listEl.id:c.el.id);a.setAttribute("aria-expanded",true)}d.touchListeners=e.on({translate:false,touchstart:d.collapseIf,scope:d,delegated:false,destroyable:true});d.scrollListeners=Ext.on({scroll:d.onGlobalScroll,scope:d,destroyable:true});Ext.on("resize",d.alignPicker,d,{buffer:1});d.fireEvent("expand",d);d.onExpand()}},onExpand:Ext.emptyFn,alignPicker:function(){if(!this.destroyed){var a=this.getPicker();if(a.isVisible()&&a.isFloating()){this.doAlign()}}},doAlign:function(){var d=this,c=d.picker,a="-above",b;d.picker.alignTo(d.triggerWrap,d.pickerAlign,d.pickerOffset);b=c.el.getY()=d){j.deselectRange(n,d-1)}else{if(l!==g){j.selectRange(l,g,b)}}}j.lastSelected=g}else{if(m){if(!b){j.doSelect(g,false)}}else{j.selectWithEvent(g,i)}}}}break;case"SIMPLE":if(m===i.A&&b){j.selected.beginUpdate();j.selectRange(0,j.store.getCount()-1);j.selected.endUpdate()}else{if(c){j.doDeselect(g)}else{j.doSelect(g,true)}}break;case"SINGLE":if(!b){if(k){j.doSelect(g,false)}else{if(o||!m){j.selectWithEvent(g,i)}}}}if(!i.shiftKey&&!j.destroyed&&j.isSelected(g)){j.selectionStart=g;j.selectionStartIdx=d}},selectRange:function(m,d,n){var j=this,l=j.store,c=j.selected.items,o,g,h,e,a,k,b;if(j.isLocked()){return}o=j.normalizeRowRange(m,d);m=o[0];d=o[1];e=[];for(g=m;g<=d;g++){if(!j.isSelected(l.getAt(g))){e.push(l.getAt(g))}}if(!n){a=[];j.suspendChanges();for(g=0,h=c.length;gd){a.push(b)}}for(g=0,h=a.length;g0)}}}},deselectRange:function(e,d){var j=this,c=j.store,a,h,g,b;if(j.isLocked()){return}a=j.normalizeRowRange(e,d);e=a[0];d=a[1];g=[];for(h=e;h<=d;h++){b=c.getAt(h);if(j.isSelected(b)){g.push(b)}}if(g.length){j.doDeselect(g)}},normalizeRowRange:function(c,b){var a=this.store,d;if(!Ext.isNumber(c)){c=a.indexOf(c)}c=Math.max(0,c);if(!Ext.isNumber(b)){b=a.indexOf(b)}b=Math.min(b,a.getCount()-1);if(c>b){d=b;b=c;c=d}return[c,b]},select:function(b,c,a){if(Ext.isDefined(b)&&!(Ext.isArray(b)&&!b.length)){this.doSelect(b,c,a)}},deselect:function(b,a){this.doDeselect(b,a)},doSelect:function(c,e,b){var d=this,a;if(d.locked||c==null){return}if(typeof c==="number"){a=d.store.getAt(c);if(!a){return}c=[a]}if(d.selectionMode==="SINGLE"){if(c.isModel){c=[c]}if(c.length){d.doSingleSelect(c[0],b)}}else{d.doMultiSelect(c,e,b)}},doMultiSelect:function(a,l,k){var h=this,b=h.selected,j=false,m,d,g,e,c;if(h.locked){return}a=!Ext.isArray(a)?[a]:a;g=a.length;if(!l&&b.getCount()>0){m=h.deselectDuringSelect(a,k);if(h.destroyed){return}if(m[0]){h.maybeFireSelectionChange(m[1]>0&&!k);return}else{j=m[1]>0}}c=function(){if(!b.getCount()){h.selectionStart=e}b.add(e);j=true};for(d=0;d0&&!k);return g===l},doSingleSelect:function(a,b){var d=this,g=false,c=d.selected,e;if(d.locked){return}if(d.isSelected(a)){return}e=function(){if(c.getCount()){d.suspendChanges();var h=d.deselectDuringSelect([a],b);if(d.destroyed){return}d.resumeChanges();if(h[0]){return false}}d.lastSelected=a;if(!c.getCount()){d.selectionStart=a}c.add(a);g=true};d.onSelectChange(a,true,b,e);if(g&&!d.destroyed){d.maybeFireSelectionChange(!b)}},maybeFireSelectionChange:function(a){var b=this;if(a&&!b.suspendChange){b.fireEvent("selectionchange",b,b.getSelection())}},getLastSelected:function(){return this.lastSelected},getSelection:function(){return this.selected.getRange()},getSelectionMode:function(){return this.selectionMode},setSelectionMode:function(a){a=a?a.toUpperCase():"SINGLE";this.selectionMode=this.modes[a]?a:"SINGLE"},isLocked:function(){return this.locked},setLocked:function(a){this.locked=!!a},isRangeSelected:function(d,c){var g=this,b=g.store,e,a;a=g.normalizeRowRange(d,c);d=a[0];c=a[1];for(e=d;e<=c;e++){if(!g.isSelected(b.getAt(e))){return false}}return true},isSelected:function(a){a=Ext.isNumber(a)?this.store.getAt(a):a;return this.selected.contains(a)},hasSelection:function(){var a=this.getSelected();return !!(a&&a.getCount())},refresh:function(){var m=this,p=m.store,h=[],l=[],g=m.getSelection(),j=g.length,c=m.getSelected(),n,k,a,o,b,e;if(!p||!(c.isCollection||c.isRows)||!c.getCount()){return}a=p.getData();if(a.getSource){k=a.getSource();if(k){a=k}}m.refreshing=true;c.beginUpdate();m.suspendChanges();for(e=0;e0);if(h){k.fireEvent("lastselectedchanged",k,k.getSelection(),k.lastSelected)}},pruneRemovedOnRefresh:function(){return this.pruneRemoved},onStoreLoad:Ext.emptyFn,onSelectChange:function(a,d,c,g){var e=this,b=d?"select":"deselect";if((c||e.fireEvent("before"+b,e,a))!==false&&g()!==false){if(!c){e.fireEvent(b,e,a)}}},onEditorKey:Ext.emptyFn,beforeViewRender:function(a){Ext.Array.include(this.views||(this.views=[]),a)},onHeaderClick:Ext.emptyFn,resolveListenerScope:function(c){var a=this.view,b;if(a){b=a.resolveSatelliteListenerScope(this,c)}return b||Ext.mixin.Observable.prototype.resolveListenerScope.call(this,c)},bindComponent:Ext.emptyFn,privates:{onBeforeNavigate:Ext.privateFn,selectWithEventMulti:function(k,m,d){var n=this,g=m.shiftKey,a=m.ctrlKey,c=g?(n.getSelectionStart()):null,h=n.getSelection(),l=h.length,b,j,o;if(g&&c){n.selectRange(c,k,a)}else{if(a&&d){if(n.allowDeselect){n.doDeselect(k,false)}}else{if(a){n.doSelect(k,true,false)}else{if(d&&!g&&!a&&l>1){if(n.allowDeselect){b=[];for(j=0;jthis.view.all.getCount()-1){a=0}this.setPosition(a,b)},onKeyRight:function(b){var a=this.recordIndex+1;if(a>this.view.all.getCount()-1){a=0}this.setPosition(a,b)},onKeyLeft:function(b){var a=this.recordIndex-1;if(a<0){a=this.view.all.getCount()-1}this.setPosition(a,b)},onKeyPageDown:Ext.emptyFn,onKeyPageUp:Ext.emptyFn,onKeyHome:function(a){this.setPosition(0,a)},onKeyEnd:function(a){this.setPosition(this.view.all.getCount()-1,a)},onKeySpace:function(a){this.fireNavigateEvent(a)},onKeyEnter:function(a){a.stopEvent();a.view.fireEvent("itemclick",a.view,a.record,a.item,a.recordIndex,a)},onSelectAllKeyPress:function(a){this.fireNavigateEvent(a)},fireNavigateEvent:function(b){var a=this;a.fireEvent("navigate",{navigationModel:a,keyEvent:b,previousRecordIndex:a.previousRecordIndex,previousRecord:a.previousRecord,previousItem:a.previousItem,recordIndex:a.recordIndex,record:a.record,item:a.item})},destroy:function(){var a=this;a.setStore(null);Ext.destroy(a.viewListeners,a.keyNav);a.keyNav=a.viewListeners=a.dataSource=a.lastFocused=null;a.callParent()}},1,0,0,0,["view.navigation.default"],[[Ext.util.Observable.prototype.mixinId||Ext.util.Observable.$className,Ext.util.Observable],[Ext.mixin.Factoryable.prototype.mixinId||Ext.mixin.Factoryable.$className,Ext.mixin.Factoryable],[Ext.util.StoreHolder.prototype.mixinId||Ext.util.StoreHolder.$className,Ext.util.StoreHolder]],[Ext.view,"NavigationModel"],0));(Ext.cmd.derive("Ext.view.AbstractView",Ext.Component,{inheritableStatics:{getRecord:function(a){return this.getBoundView(a).getRecord(a)},getBoundView:function(a){return Ext.getCmp(a.getAttribute("data-boundView"))}},defaultBindProperty:"store",renderBuffer:document.createElement("div"),statics:{updateDelay:200,queueRecordChange:function(n,p,h,b,c){var m=this,a=m.changeQueue||(m.changeQueue={}),j=h.internalId,l,g,k,d,q,o,e;l=a[j]||(a[j]={operation:b,record:h,data:{},views:[]});g=l.data;Ext.Array.include(l.views,n);if(c&&(k=c.length)){for(d=0;d
    {1}
    ',d.itemCls,e,d.itemAriaRole);d.tpl=new Ext.XTemplate(e,c)}Ext.Component.prototype.initComponent.call(this);d.tpl=d.getTpl("tpl");if(d.overItemCls){d.trackOver=true}d.addCmpEvents();a=d.store=Ext.data.StoreManager.lookup(d.store||"ext-empty-store");if(!d.dataSource){d.dataSource=a}d.bindStore(a,true);d.getNavigationModel().bindComponent(this);if(!d.all){d.all=new Ext.CompositeElementLite()}d.scrollState={top:0,left:0};d.savedTabIndexAttribute="data-savedtabindex-"+d.id},getElConfig:function(){var a=this.mixins.renderable.getElConfig.call(this);if(this.focusable){a.tabIndex=0}return a},onRender:function(){var a=this.loadMask;Ext.Component.prototype.onRender.apply(this,arguments);if(a){this.createMask(a)}},beforeLayout:function(){var a=this;Ext.Component.prototype.beforeLayout.apply(this,arguments);if(a.refreshNeeded&&!a.pendingRefresh){if(a.refreshCounter){a.refresh()}else{a.doFirstRefresh(a.dataSource)}}},onMaskBeforeShow:function(){var b=this,a=b.loadingHeight;if(a&&a>b.getHeight()){b.hasLoadingHeight=true;b.oldMinHeight=b.minHeight;b.minHeight=a;b.updateLayout()}},onMaskHide:function(){var a=this;if(!a.destroying&&a.hasLoadingHeight){a.minHeight=a.oldMinHeight;a.updateLayout();delete a.hasLoadingHeight}},beforeRender:function(){Ext.Component.prototype.beforeRender.apply(this,arguments);this.getSelectionModel().beforeViewRender(this)},afterRender:function(){Ext.Component.prototype.afterRender.apply(this,arguments);if(this.focusable){this.focusEl=this.el}},getRefItems:function(){var b=this.loadMask,a=[];if(b&&b.isComponent){a.push(b)}return a},getSelection:function(){return this.getSelectionModel().getSelection()},updateSelection:function(a){var b=this,c;if(!b.ignoreNextSelection){b.ignoreNextSelection=true;c=b.getSelectionModel();if(a){c.select(a)}else{c.deselectAll()}b.ignoreNextSelection=false}},updateBindSelection:function(a,c){var d=this,b=null;if(!d.ignoreNextSelection){d.ignoreNextSelection=true;if(c.length){b=a.getLastSelected();d.hasHadSelection=true}if(d.hasHadSelection){d.setSelection(b)}d.ignoreNextSelection=false}},applySelectionModel:function(b,g){var e=this,d=e.grid,h,c,a;if(g){g.un({scope:e,selectionchange:e.updateBindSelection,lastselectedchanged:e.updateBindSelection,select:e.ariaSelect,deselect:e.ariaDeselect});Ext.destroy(e.selModelRelayer);b=Ext.Factory.selection(b)}else{if(b&&b.isSelectionModel){b.locked=e.disableSelection}else{if(e.simpleSelect){h="SIMPLE"}else{if(e.multiSelect){h="MULTI"}else{h="SINGLE"}}if(typeof b==="string"){b={type:b}}b=Ext.Factory.selection(Ext.apply({allowDeselect:e.allowDeselect||e.multiSelect,mode:h,locked:e.disableSelection},b))}}if(b.mode!=="SINGLE"){a=(d||e).ariaEl.dom;if(a){a.setAttribute("aria-multiselectable",true)}else{if(!d){c=e.ariaRenderAttributes||(e.ariaRenderAttributes={});c["aria-multiselectable"]=true}}}e.selModelRelayer=e.relayEvents(b,["selectionchange","beforeselect","beforedeselect","select","deselect","focuschange"]);b.on({scope:e,lastselectedchanged:e.updateBindSelection,selectionchange:e.updateBindSelection,select:e.ariaSelect,deselect:e.ariaDeselect});return b},updateSelectionModel:function(a){this.selModel=a},applyNavigationModel:function(a){return Ext.Factory.viewNavigation(a)},onFocusEnter:function(d){var c=this,b=c.getNavigationModel(),a;c.toggleChildrenTabbability(false);if(!c.itemFocused&&c.all.getCount()){a=b.getLastFocused();b.setPosition(a||0,d.event,null,!a);c.itemFocused=b.getPosition()!=null}if(c.itemFocused){this.el.dom.setAttribute("tabIndex","-1")}Ext.Component.prototype.onFocusEnter.call(this,d)},onFocusLeave:function(b){var a=this;if(a.itemFocused&&!a.refreshing){a.getNavigationModel().setPosition(null,b.event,null,true);a.itemFocused=false;a.el.dom.setAttribute("tabIndex",0)}Ext.Component.prototype.onFocusLeave.call(this,b)},ariaSelect:function(b,a){var c=this.getNode(a);if(c){c.setAttribute("aria-selected",true)}},ariaDeselect:function(b,a){var c=this.getNode(a);if(c){c.removeAttribute("aria-selected")}},onRemoved:function(a){Ext.Component.prototype.onRemoved.call(this,a);if(!a){this.onFocusLeave({})}},refresh:function(){var j=this,i=j.all,l=i.getCount(),h=j.refreshCounter,k,c,b,g=j.getSelectionModel(),e,d=h&&i.getCount()&&j.preserveScrollOnRefresh&&j.getScrollable(),a;if(!j.rendered||j.destroyed){return}if(!j.hasListeners.beforerefresh||j.fireEvent("beforerefresh",j)!==false){j.refreshing=true;e=j.saveFocusState();k=j.getTargetEl();b=j.getViewRange();c=k.dom;if(d){a=d.getPosition();if(!(a.x||a.y)){a=null}}if(h){j.clearViewEl();j.refreshCounter++}else{j.refreshCounter=1}j.tpl.append(k,j.collectData(b,i.startIndex||0));if(b.length<1){j.addEmptyText();i.clear()}else{j.collectNodes(k.dom);j.updateIndexes(0)}e();if(j.refreshSelmodelOnRefresh!==false){g.refresh()}j.refreshNeeded=false;j.refreshSize(i.getCount()!==l);j.fireEvent("refresh",j,b);if(d){d.scrollTo(a)}if(!j.viewReady){j.viewReady=true;j.fireEvent("viewready",j)}j.refreshing=false;j.refreshScroll();j.cleanupData()}},addEmptyText:function(){var b=this,a=b.getStore();if(b.emptyText&&!a.isLoading()&&(!b.deferEmptyText||b.refreshCounter>1||a.isLoaded())){b.emptyEl=Ext.core.DomHelper.insertHtml("beforeEnd",b.getTargetEl().dom,b.emptyText)}},getViewRange:function(){return this.dataSource.getRange()},refreshSize:function(d){var c=this,b=c.getSizeModel(),a=c.getScrollable();if(b.height.shrinkWrap||b.width.shrinkWrap||d){c.updateLayout()}else{if(c.touchScroll&&!c.bufferedRenderer){if(a){a.refresh()}else{c.on({boxready:c.refreshScroll,scope:c,single:true})}}}},afterFirstLayout:function(c,b){var d=this,a=d.getScrollable();if(a){a.on({scroll:d.onViewScroll,scrollend:d.onViewScrollEnd,scope:d,onFrame:!!Ext.global.requestAnimationFrame})}Ext.Component.prototype.afterFirstLayout.call(this,c,b)},clearViewEl:function(){var b=this,c=b.getTargetEl(),a=b.getNodeContainer()===c;b.clearEmptyEl();b.all.clear(!a);if(a){c.dom.innerHTML=""}},clearEmptyEl:function(){var a=this.emptyEl;if(a){Ext.removeNode(a)}this.emptyEl=null},onViewScroll:function(b,a,c){this.fireEvent("scroll",this,a,c)},onViewScrollEnd:function(b,a,c){this.fireEvent("scrollend",this,a,c)},saveScrollState:function(){var a=this,b=a.scrollState;if(a.rendered){b.left=a.getScrollX();b.top=a.getScrollY()}},restoreScrollState:function(){var a=this,b=a.scrollState;if(a.rendered){a.setScrollX(b.left);a.setScrollY(b.top)}},prepareData:function(e,d,c){var b,a,g;if(c){b=c.getAssociatedData();for(a in b){if(b.hasOwnProperty(a)){if(!g){e=Ext.Object.chain(e);g=true}e[a]=b[a]}}}return e},collectData:function(c,g){var e=[],d=0,a=c.length,b;for(;d-1){if(g.getNode(a)){e=g.bufferRender([a],d).children[0];g.all.replaceElement(d,e,true);g.updateIndexes(d,d);b.onUpdate(a);g.refreshSizePending=true;if(b.isSelected(a)){g.onItemSelect(a)}if(g.hasListeners.itemupdate){g.fireEvent("itemupdate",a,d,e)}return e}}}},onReplace:function(k,m,b,c){var i=this,j=i.all,g=i.getSelectionModel(),n=m,p,o,h,a,l,d,e;if(i.rendered){p=i.bufferRender(c,m,true);h=p.fragment;a=p.children;o=j.item(m);if(o){j.item(m).insertSibling(h,"before",true)}else{i.appendNodes(h)}j.insert(m,a);if(b.length){e=i.saveFocusState()}m+=c.length;d=m+b.length-1;l=j.removeRange(m,d,true);if(i.refreshSelmodelOnRefresh!==false){g.refresh()}i.updateIndexes(m);if(i.hasListeners.itemremove){i.fireEvent("itemremove",b,n,l,i)}if(i.hasListeners.itemadd){i.fireEvent("itemadd",c,n,a)}e();i.refreshSize()}},onAdd:function(d,c,e){var g=this,b,a=g.getSelectionModel();if(g.rendered){if(g.all.getCount()===0){g.refresh();b=g.all.slice()}else{b=g.doAdd(c,e);if(g.refreshSelmodelOnRefresh!==false){a.refresh()}g.updateIndexes(e);g.refreshSizePending=true}if(g.hasListeners.itemadd){g.fireEvent("itemadd",c,e,b)}}},appendNodes:function(a){var b=this.all,c=b.getCount();if(this.nodeContainerSelector){this.getNodeContainer().appendChild(a)}else{b.item(c-1).insertSibling(a,"after")}},doAdd:function(c,e){var h=this,k=h.bufferRender(c,e,true),g=k.fragment,b=k.children,i=h.all,d=i.getCount(),j=i.startIndex||0,a=i.endIndex||d-1;if(d===0||e>a){h.appendNodes(g)}else{if(e<=j){i.item(j).insertSibling(g,"before",true)}else{i.item(e).insertSibling(b,"before",true)}}i.insert(e,b);return b},onRemove:function(m,d,j){var k=this,n=k.all,b=k.hasListeners.itemremove,l,e,g,a,c,h;if(n.getCount()){if(k.dataSource.getCount()===0){if(b){k.fireEvent("itemremove",d,j,k.getNodes(j,j+d.length-1))}k.refresh()}else{h=k.saveFocusState();if(b){a=[]}for(e=d.length-1;e>=0;--e){g=d[e];l=j+e;if(a){c=n.item(l);a[e]=c?c.dom:undefined}if(n.item(l)){k.doRemove(g,l)}}if(b){k.fireEvent("itemremove",d,j,a,k)}h();k.updateIndexes(j)}k.refreshSizePending=true}},doRemove:function(a,b){this.all.removeElement(b,true)},saveFocusState:function(){var d=this,a=d.dataSource||d.store,b=d.getNavigationModel(),c=b.recordIndex,e=b.record;if(d.el.contains(Ext.Element.getActiveElement())){d.el.dom.focus();return function(){if(a.getCount()){c=Math.min(c,d.all.getCount()-1);b.setPosition(a.contains(e)?e:c,null,null,true)}}}return Ext.emptyFn},refreshNode:function(a){if(Ext.isNumber(a)){a=this.store.getAt(a)}this.onUpdate(this.dataSource,a)},updateIndexes:function(h,g){var b=this.all.elements,e,a=this.getViewRange(),d,c=this.id;h=h||0;g=g||((g===0)?0:(b.length-1));for(d=h;d<=g;d++){e=b[d];e.setAttribute("data-recordIndex",d);e.setAttribute("data-recordId",a[d].internalId);e.setAttribute("data-boundView",c)}},bindStore:function(b,c){var e=this,a=e.getSelectionModel(),d=e.getNavigationModel();a.bindStore(b);a.bindComponent(b?e:null);e.mixins.storeholder.bindStore.apply(e,arguments);d.setStore(b);if(b&&e.componentLayoutCounter){e.doFirstRefresh(b,!c)}},doFirstRefresh:function(a,c){var b=this;if(b.deferInitialRefresh&&!c){Ext.defer(b.doFirstRefresh,1,b,[a,true])}else{if(a&&!a.isLoading()){b.refresh()}}},onUnbindStore:function(a){this.setMaskBind(null);if(this.dataSource===a){this.dataSource=null}},onBindStore:function(a,c){var b=this;if(b.store.isBufferedStore){b.store.preserveScrollOnReload=b.preserveScrollOnReload}if(c&&c.isBufferedStore){delete c.preserveScrollOnReload}b.setMaskBind(a);if(!b.dataSource){b.dataSource=a}},setMaskBind:function(b){var a=this.loadMask;if(this.rendered&&a&&b&&!a.bindStore){a=this.createMask()}if(a&&a.bindStore){a.bindStore(b)}},getStoreListeners:function(){var a=this;return{refresh:a.onDataRefresh,replace:a.onReplace,add:a.onAdd,remove:a.onRemove,update:a.onUpdate,clear:a.onDataRefresh,beginupdate:a.onBeginUpdate,endupdate:a.onEndUpdate}},onBeginUpdate:function(){++this.updateSuspendCounter;Ext.suspendLayouts()},onEndUpdate:function(){var a=this;if(a.updateSuspendCounter){--a.updateSuspendCounter}Ext.resumeLayouts(true);if(a.refreshSizePending){a.refreshSize(true);a.refreshSizePending=false}},onDataRefresh:function(a){var c=this,b=c.preserveScrollOnRefresh;if(a.loadCount>c.lastRefreshLoadCount){c.preserveScrollOnRefresh=c.preserveScrollOnReLoad}c.refreshView();c.preserveScrollOnRefresh=b;c.lastRefreshLoadCount=a.loadCount},refreshView:function(){var b=this,a=b.blockRefresh||!b.rendered||b.up("[collapsed],[isCollapsingOrExpanding],[hidden]");if(a){b.refreshNeeded=true}else{if(b.bufferedRenderer){b.bufferedRenderer.refreshView()}else{b.refresh()}}},findItemByChild:function(a){return Ext.fly(a).findParent(this.getItemSelector(),this.getTargetEl())},findTargetByEvent:function(a){return a.getTarget(this.getItemSelector(),this.getTargetEl())},getSelectedNodes:function(){var b=[],a=this.getSelectionModel().getSelection(),d=a.length,c=0;for(;ch.bottom){a=c.bottom-h.bottom}}if(c.lefth.right){b=c.right-h.right}}if(b||a){g.scrollBy(b,a,false)}e.set({tabIndex:-1});e.focus()}},privates:{repaintBorder:function(b){var a=this.getNode(b);if(a){a.className=a.className}}}},0,["dataview"],["component","box","dataview"],{component:true,box:true,dataview:true},["widget.dataview"],0,[Ext.view,"View",Ext,"DataView"],0));(Ext.cmd.derive("Ext.view.BoundListKeyNav",Ext.view.NavigationModel,{navigateOnSpace:true,initKeyNav:function(a){var b=this,c=a.pickerField;if(!b.keyNav){Ext.view.NavigationModel.prototype.initKeyNav.call(this,a);b.keyNav.map.addBinding({key:Ext.event.Event.ESC,fn:b.onKeyEsc,scope:b})}if(!c){return}if(!c.rendered){c.on("render",Ext.Function.bind(b.initKeyNav,b,[a],0),b,{single:true});return}b.fieldKeyNav=new Ext.util.KeyNav({disabled:true,target:c.inputEl,forceKeyDown:true,up:b.onKeyUp,down:b.onKeyDown,right:b.onKeyRight,left:b.onKeyLeft,pageDown:b.onKeyPageDown,pageUp:b.onKeyPageUp,home:b.onKeyHome,end:b.onKeyEnd,tab:b.onKeyTab,space:b.onKeySpace,enter:b.onKeyEnter,A:{ctrl:true,handler:b.onSelectAllKeyPress},priority:1001,scope:b})},processViewEvent:function(b,a,e,c,d){if(d.within(b.listWrap)){return d}if(d.getKey()===d.ESC){if(Ext.fly(d.target).isInputField()){d.target=d.target.parentNode}return d}},enable:function(){this.fieldKeyNav.enable();Ext.view.NavigationModel.prototype.enable.call(this)},disable:function(){this.fieldKeyNav.disable();Ext.view.NavigationModel.prototype.disable.call(this)},onItemMouseDown:function(b,a,e,c,d){Ext.view.NavigationModel.prototype.onItemMouseDown.call(this,b,a,e,c,d);d.preventDefault()},onKeyUp:function(i){var g=this,b=g.view,d=b.all,h=b.highlightedItem,c=h?b.indexOf(h):-1,a=c>0?c-1:d.getCount()-1;g.setPosition(a);i.preventDefault()},onKeyDown:function(i){var g=this,b=g.view,d=b.all,h=b.highlightedItem,c=h?b.indexOf(h):-1,a=c
    ',destroy:function(){var a=this;if(a.spinnerEl){a.spinnerEl.destroy();a.spinnerEl=a.upEl=a.downEl=null}Ext.form.trigger.Trigger.prototype.destroy.call(this)},getBodyRenderData:function(){var a=this;return{vertical:a.vertical,upDisabledCls:a.upEnabled?"":(a.spinnerUpCls+"-disabled"),downDisabledCls:a.downEnabled?"":(a.spinnerDownCls+"-disabled"),spinnerCls:a.spinnerCls,spinnerUpCls:a.spinnerUpCls,spinnerDownCls:a.spinnerDownCls}},getStateEl:function(){return this.spinnerEl},onClick:function(){var b=this,a=arguments,d=b.clickRepeater?a[1]:a[0],c=b.field;if(!c.readOnly&&!c.disabled){if(b.upEl.contains(d.target)){Ext.callback(b.upHandler,b.scope,[c,b,d],0,c)}else{if(b.downEl.contains(d.target)){Ext.callback(b.downHandler,b.scope,[c,b,d],0,c)}}}c.inputEl.focus()},onFieldRender:function(){var b=this,a=b.vertical,d,c;Ext.form.trigger.Trigger.prototype.onFieldRender.call(this);d=b.spinnerEl=b.el.select("."+b.spinnerCls,true);c=d.elements;b.upEl=a?c[0]:c[1];b.downEl=a?c[1]:c[0]},setUpEnabled:function(a){this.upEl[a?"removeCls":"addCls"](this.spinnerUpCls+"-disabled")},setDownEnabled:function(a){this.downEl[a?"removeCls":"addCls"](this.spinnerDownCls+"-disabled")}},0,0,0,0,["trigger.spinner"],0,[Ext.form.trigger,"Spinner"],0));(Ext.cmd.derive("Ext.form.field.Spinner",Ext.form.field.Text,{alternateClassName:"Ext.form.Spinner",config:{triggers:{spinner:{type:"spinner",upHandler:"onSpinnerUpClick",downHandler:"onSpinnerDownClick",scope:"this"}}},spinUpEnabled:true,spinDownEnabled:true,keyNavEnabled:true,mouseWheelEnabled:true,repeatTriggerClick:true,onSpinUp:Ext.emptyFn,onSpinDown:Ext.emptyFn,ariaRole:"spinbutton",applyTriggers:function(b){var c=this,a=b.spinner;a.upEnabled=c.spinUpEnabled;a.downEnabled=c.spinDownEnabled;return Ext.form.field.Text.prototype.applyTriggers.call(this,b)},onRender:function(){var b=this,a=b.getTrigger("spinner");(arguments.callee.$previous||Ext.form.field.Text.prototype.onRender).call(this);if(b.keyNavEnabled){b.spinnerKeyNav=new Ext.util.KeyNav(b.inputEl,{scope:b,up:b.spinUp,down:b.spinDown})}if(b.mouseWheelEnabled){b.mon(b.bodyEl,"mousewheel",b.onMouseWheel,b)}b.spinUpEl=a.upEl;b.spinDownEl=a.downEl},onSpinnerUpClick:function(){this.spinUp()},onSpinnerDownClick:function(){this.spinDown()},spinUp:function(){var a=this;if(a.spinUpEnabled&&!a.disabled){a.fireEvent("spin",a,"up");a.fireEvent("spinup",a);a.onSpinUp()}},spinDown:function(){var a=this;if(a.spinDownEnabled&&!a.disabled){a.fireEvent("spin",a,"down");a.fireEvent("spindown",a);a.onSpinDown()}},setSpinUpEnabled:function(a){var b=this,c=b.spinUpEnabled;b.spinUpEnabled=a;if(c!==a&&b.rendered){b.getTrigger("spinner").setUpEnabled(a)}},setSpinDownEnabled:function(a){var b=this,c=b.spinDownEnabled;b.spinDownEnabled=a;if(c!==a&&b.rendered){b.getTrigger("spinner").setDownEnabled(a)}},onMouseWheel:function(b){var a=this,c;if(a.hasFocus){c=b.getWheelDelta();if(c>0){a.spinUp()}else{if(c<0){a.spinDown()}}b.stopEvent()}},onDestroy:function(){Ext.destroyMembers(this,"spinnerKeyNav");Ext.form.field.Text.prototype.onDestroy.call(this)}},0,["spinnerfield"],["component","box","field","textfield","spinnerfield"],{component:true,box:true,field:true,textfield:true,spinnerfield:true},["widget.spinnerfield"],0,[Ext.form.field,"Spinner",Ext.form,"Spinner"],0));(Ext.cmd.derive("Ext.form.field.Number",Ext.form.field.Spinner,{alternateClassName:["Ext.form.NumberField","Ext.form.Number"],allowExponential:true,allowDecimals:true,decimalSeparator:null,submitLocaleSeparator:true,decimalPrecision:2,minValue:Number.NEGATIVE_INFINITY,maxValue:Number.MAX_VALUE,step:1,minText:"The minimum value for this field is {0}",maxText:"The maximum value for this field is {0}",nanText:"{0} is not a valid number",negativeText:"The value cannot be negative",baseChars:"0123456789",autoStripChars:false,initComponent:function(){var a=this;if(a.decimalSeparator===null){a.decimalSeparator=Ext.util.Format.decimalSeparator}Ext.form.field.Spinner.prototype.initComponent.call(this);a.setMinValue(a.minValue);a.setMaxValue(a.maxValue)},getSubTplData:function(c){var e=this,d=e.minValue,a=e.maxValue,h,b,g;h=Ext.form.field.Spinner.prototype.getSubTplData.call(this,c);b=h.inputElAriaAttributes;if(b){if(d>Number.NEGATIVE_INFINITY){b["aria-valuemin"]=d}if(a=d&&g<=a){b["aria-valuenow"]=g}}return h},setValue:function(c){var b=this,d,a;if(b.hasFocus){d=b.getBind();a=d&&d.value;if(a&&a.syncing&&c===b.value){return b}}return Ext.form.field.Spinner.prototype.setValue.call(this,c)},getErrors:function(c){c=arguments.length>0?c:this.processRawValue(this.getRawValue());var b=this,e=Ext.form.field.Spinner.prototype.getErrors.call(this,c),d=Ext.String.format,a;if(c.length<1){return e}c=String(c).replace(b.decimalSeparator,".");if(isNaN(c)){e.push(d(b.nanText,c))}a=b.parseValue(c);if(b.minValue===0&&a<0){e.push(this.negativeText)}else{if(ab.maxValue){e.push(d(b.maxText,b.maxValue))}return e},rawToValue:function(b){var a=this.fixPrecision(this.parseValue(b));if(a===null){a=b||null}return a},valueToRaw:function(c){var b=this,a=b.decimalSeparator;c=b.parseValue(c);c=b.fixPrecision(c);c=Ext.isNumber(c)?c:parseFloat(String(c).replace(a,"."));c=isNaN(c)?"":String(c).replace(".",a);return c},getSubmitValue:function(){var a=this,b=Ext.form.field.Spinner.prototype.getSubmitValue.call(this);if(!a.submitLocaleSeparator){b=b.replace(a.decimalSeparator,".")}return b},onChange:function(b){var a=this.ariaEl.dom;this.toggleSpinners();Ext.form.field.Spinner.prototype.onChange.apply(this,arguments);if(a){if(Ext.isNumber(b)&&isFinite(b)){a.setAttribute("aria-valuenow",b)}else{a.removeAttribute("aria-valuenow")}}},toggleSpinners:function(){var c=this,d=c.getValue(),b=d===null,a;if(c.spinUpEnabled||c.spinUpDisabledByToggle){a=b||dc.minValue;c.setSpinDownEnabled(a,true)}},setMinValue:function(d){var b=this,a=b.ariaEl.dom,c,e,a;b.minValue=c=Ext.Number.from(d,Number.NEGATIVE_INFINITY);b.toggleSpinners();if(a){if(c>Number.NEGATIVE_INFINITY){a.setAttribute("aria-valuemin",c)}else{a.removeAttribute("aria-valuemin")}}if(b.disableKeyFilter!==true){e=b.baseChars+"";if(b.allowExponential){e+=b.decimalSeparator+"e+-"}else{if(b.allowDecimals){e+=b.decimalSeparator}if(b.minValue<0){e+="-"}}e=Ext.String.escapeRegex(e);b.maskRe=new RegExp("["+e+"]");if(b.autoStripChars){b.stripCharsRe=new RegExp("[^"+e+"]","gi")}}},setMaxValue:function(b){var a=this.ariaEl.dom,c;this.maxValue=c=Ext.Number.from(b,Number.MAX_VALUE);if(a){if(ca.maxLength){return}}a.setValue(b)}},0,["numberfield"],["component","box","field","textfield","spinnerfield","numberfield"],{component:true,box:true,field:true,textfield:true,spinnerfield:true,numberfield:true},["widget.numberfield"],0,[Ext.form.field,"Number",Ext.form,"NumberField",Ext.form,"Number"],0));(Ext.cmd.derive("Ext.toolbar.Paging",Ext.toolbar.Toolbar,{alternateClassName:"Ext.PagingToolbar",displayInfo:false,prependButtons:false,displayMsg:"Displaying {0} - {1} of {2}",emptyMsg:"No data to display",beforePageText:"Page",afterPageText:"of {0}",firstText:"First Page",prevText:"Previous Page",nextText:"Next Page",lastText:"Last Page",refreshText:"Refresh",inputItemWidth:30,emptyPageData:{total:0,currentPage:0,pageCount:0,toRecord:0,fromRecord:0},defaultBindProperty:"store",getPagingItems:function(){var b=this,a={scope:b,blur:b.onPagingBlur};a[Ext.supports.SpecialKeyDownRepeat?"keydown":"keypress"]=b.onPagingKeyDown;return[{itemId:"first",tooltip:b.firstText,overflowText:b.firstText,iconCls:"x-tbar-page-first",disabled:true,handler:b.moveFirst,scope:b},{itemId:"prev",tooltip:b.prevText,overflowText:b.prevText,iconCls:"x-tbar-page-prev",disabled:true,handler:b.movePrevious,scope:b},"-",b.beforePageText,{xtype:"numberfield",itemId:"inputItem",name:"inputItem",cls:"x-tbar-page-number",allowDecimals:false,minValue:1,hideTrigger:true,enableKeyEvents:true,keyNavEnabled:false,selectOnFocus:true,submitValue:false,isFormField:false,width:b.inputItemWidth,margin:"-1 2 3 2",listeners:a},{xtype:"tbtext",itemId:"afterTextItem",html:Ext.String.format(b.afterPageText,1)},"-",{itemId:"next",tooltip:b.nextText,overflowText:b.nextText,iconCls:"x-tbar-page-next",disabled:true,handler:b.moveNext,scope:b},{itemId:"last",tooltip:b.lastText,overflowText:b.lastText,iconCls:"x-tbar-page-last",disabled:true,handler:b.moveLast,scope:b},"-",{itemId:"refresh",tooltip:b.refreshText,overflowText:b.refreshText,iconCls:"x-tbar-loading",disabled:b.store.isLoading(),handler:b.doRefresh,scope:b}]},initComponent:function(){var b=this,a=b.items||b.buttons||[],c;b.bindStore(b.store||"ext-empty-store",true);c=b.getPagingItems();if(b.prependButtons){b.items=a.concat(c)}else{b.items=c.concat(a)}delete b.buttons;if(b.displayInfo){b.items.push("->");b.items.push({xtype:"tbtext",itemId:"displayItem"})}Ext.toolbar.Toolbar.prototype.initComponent.call(this)},beforeRender:function(){Ext.toolbar.Toolbar.prototype.beforeRender.apply(this,arguments);this.updateBarInfo()},updateBarInfo:function(){var a=this;if(!a.store.isLoading()){a.calledInternal=true;a.onLoad();a.calledInternal=false}},updateInfo:function(){var e=this,c=e.child("#displayItem"),a=e.store,b=e.getPageData(),d,g;if(c){d=a.getCount();if(d===0){g=e.emptyMsg}else{g=Ext.String.format(e.displayMsg,b.fromRecord,b.toRecord,b.total)}c.setText(g)}},onLoad:function(){var h=this,d,b,c,a,g,i,e;g=h.store.getCount();i=g===0;if(!i){d=h.getPageData();b=d.currentPage;c=d.pageCount;if(b>c){if(c>0){h.store.loadPage(c)}else{h.getInputItem().reset()}return}a=Ext.String.format(h.afterPageText,isNaN(c)?1:c)}else{b=0;c=0;a=Ext.String.format(h.afterPageText,0)}Ext.suspendLayouts();e=h.child("#afterTextItem");if(e){e.update(a)}e=h.getInputItem();if(e){e.setDisabled(i).setValue(b)}h.setChildDisabled("#first",b===1||i);h.setChildDisabled("#prev",b===1||i);h.setChildDisabled("#next",b===c||i);h.setChildDisabled("#last",b===c||i);h.setChildDisabled("#refresh",false);h.updateInfo();Ext.resumeLayouts(true);if(!h.calledInternal){h.fireEvent("change",h,d||h.emptyPageData)}},setChildDisabled:function(a,b){var c=this.child(a);if(c){c.setDisabled(b)}},getPageData:function(){var b=this.store,a=b.getTotalCount();return{total:a,currentPage:b.currentPage,pageCount:Math.ceil(a/b.pageSize),fromRecord:((b.currentPage-1)*b.pageSize)+1,toRecord:Math.min(b.currentPage*b.pageSize,a)}},onLoadError:function(){this.setChildDisabled("#refresh",false)},getInputItem:function(){return this.child("#inputItem")},readPageFromInput:function(b){var c=this.getInputItem(),d=false,a;if(c){a=c.getValue();d=parseInt(a,10);if(!a||isNaN(d)){c.setValue(b.currentPage);return false}}return d},onPagingBlur:function(c){var b=this.getInputItem(),a;if(b){a=this.getPageData().currentPage;b.setValue(a)}},onPagingKeyDown:function(b,a){this.processKeyEvent(b,a)},processKeyEvent:function(i,h){var d=this,c=h.getKey(),b=d.getPageData(),a=h.shiftKey?10:1,g;if(c===h.RETURN){h.stopEvent();g=d.readPageFromInput(b);if(g!==false){g=Math.min(Math.max(1,g),b.pageCount);if(g!==b.currentPage&&d.fireEvent("beforechange",d,g)!==false){d.store.loadPage(g)}}}else{if(c===h.HOME||c===h.END){h.stopEvent();g=c===h.HOME?1:b.pageCount;i.setValue(g)}else{if(c===h.UP||c===h.PAGE_UP||c===h.DOWN||c===h.PAGE_DOWN){h.stopEvent();g=d.readPageFromInput(b);if(g){if(c===h.DOWN||c===h.PAGE_DOWN){a*=-1}g+=a;if(g>=1&&g<=b.pageCount){i.setValue(g)}}}}}},beforeLoad:function(){this.setChildDisabled("#refresh",true)},moveFirst:function(){if(this.fireEvent("beforechange",this,1)!==false){this.store.loadPage(1);return true}return false},movePrevious:function(){var c=this,a=c.store,b=a.currentPage-1;if(b>0){if(c.fireEvent("beforechange",c,b)!==false){a.previousPage();return true}}return false},moveNext:function(){var d=this,a=d.store,c=d.getPageData().pageCount,b=a.currentPage+1;if(b<=c){if(d.fireEvent("beforechange",d,b)!==false){a.nextPage();return true}}return false},moveLast:function(){var b=this,a=b.getPageData().pageCount;if(b.fireEvent("beforechange",b,a)!==false){b.store.loadPage(a);return true}return false},doRefresh:function(){var b=this,a=b.store,c=a.currentPage;if(b.fireEvent("beforechange",b,c)!==false){a.loadPage(c);return true}return false},getStoreListeners:function(){return{beforeload:this.beforeLoad,load:this.onLoad,exception:this.onLoadError}},onBindStore:function(){if(this.rendered){this.updateBarInfo()}},onDestroy:function(){this.bindStore(null);Ext.toolbar.Toolbar.prototype.onDestroy.call(this)}},0,["pagingtoolbar"],["component","box","container","toolbar","pagingtoolbar"],{component:true,box:true,container:true,toolbar:true,pagingtoolbar:true},["widget.pagingtoolbar"],[[Ext.util.StoreHolder.prototype.mixinId||Ext.util.StoreHolder.$className,Ext.util.StoreHolder]],[Ext.toolbar,"Paging",Ext,"PagingToolbar"],0));(Ext.cmd.derive("Ext.view.BoundList",Ext.view.View,{alternateClassName:"Ext.BoundList",pageSize:0,baseCls:"x-boundlist",itemCls:"x-boundlist-item",listItemCls:"",shadow:false,trackOver:true,preserveScrollOnRefresh:true,enableInitialSelection:false,refreshSelmodelOnRefresh:true,componentLayout:"boundlist",navigationModel:"boundlist",scrollable:true,ariaEl:"listEl",tabIndex:-1,childEls:["listWrap","listEl"],renderTpl:['
    ','
      {$}="{.}"',">","
    ","
    ","{%","var pagingToolbar=values.$comp.pagingToolbar;","if (pagingToolbar) {","Ext.DomHelper.generateMarkup(pagingToolbar.getRenderTree(), out);","}","%}",{disableFormats:true}],focusOnToFront:false,initComponent:function(){var b=this,a=b.baseCls,c=b.itemCls;b.selectedItemCls=a+"-selected";if(b.trackOver){b.overItemCls=a+"-item-over"}b.itemSelector="."+c;b.scrollerSelector="ul.x-list-plain";if(b.floating){b.addCls(a+"-floating")}if(!b.tpl){b.tpl=new Ext.XTemplate('','
  • '+b.getInnerTpl(b.displayField)+"
  • ","
    ")}else{if(!b.tpl.isTemplate){b.tpl=new Ext.XTemplate(b.tpl)}}if(b.pageSize){b.pagingToolbar=b.createPagingToolbar()}Ext.view.View.prototype.initComponent.call(this)},getRefOwner:function(){return this.pickerField||Ext.view.View.prototype.getRefOwner.call(this)},getRefItems:function(){var a=Ext.view.View.prototype.getRefItems.call(this),b=this.pagingToolbar;if(b){a.push(b)}return a},createPagingToolbar:function(){return Ext.widget("pagingtoolbar",{id:this.id+"-paging-toolbar",pageSize:this.pageSize,store:this.dataSource,border:false,ownerCt:this,ownerLayout:this.getComponentLayout()})},getNodeContainer:function(){return this.listEl},refresh:function(){var b=this,a=b.tpl;a.field=b.pickerField;a.store=b.store;Ext.view.View.prototype.refresh.call(this);a.field=a.store=null},bindStore:function(a,b){var c=this.pagingToolbar;Ext.view.View.prototype.bindStore.apply(this,arguments);if(c){c.bindStore(a,b)}},getInnerTpl:function(a){return"{"+a+"}"},onShow:function(){var a=this.pickerField;Ext.view.View.prototype.onShow.call(this);if(a&&a.rendered&&!a.hasFocus){a.focus()}},afterComponentLayout:function(c,a,b,e){var d=this.pickerField;Ext.view.View.prototype.afterComponentLayout.call(this,c,a,b,e);if(d&&d.alignPicker){d.alignPicker()}},onItemClick:function(a){var d=this,e=d.pickerField,b,c;if(!e){return}b=e.valueField;c=d.getSelectionModel().getSelection();if(!e.multiSelect&&c.length){c=c[0];if(c&&e.isEqual(a.get(b),c.get(b))&&e.collapse){e.collapse()}}},onContainerClick:function(b){var a=this.pagingToolbar;if(a&&a.rendered&&b.within(a.el)){return false}},onDestroy:function(){Ext.view.View.prototype.onDestroy.call(this);Ext.destroyMembers(this,"pagingToolbar","listWrap","listEl")},privates:{getTargetEl:function(){return this.listEl},getOverflowEl:function(){return this.listWrap},finishRenderChildren:function(){var a=this.pagingToolbar;Ext.view.View.prototype.finishRenderChildren.apply(this,arguments);if(a){a.finishRender()}}}},0,["boundlist"],["component","box","dataview","boundlist"],{component:true,box:true,dataview:true,boundlist:true},["widget.boundlist"],[[Ext.mixin.Queryable.prototype.mixinId||Ext.mixin.Queryable.$className,Ext.mixin.Queryable]],[Ext.view,"BoundList",Ext,"BoundList"],0));(Ext.cmd.derive("Ext.form.field.ComboBox",Ext.form.field.Picker,{alternateClassName:"Ext.form.ComboBox",config:{filters:null,selection:null,valueNotFoundText:null,displayTpl:false,delimiter:", ",displayField:"text"},publishes:["selection"],twoWayBindable:["selection"],triggerCls:"x-form-arrow-trigger",hiddenName:"",collapseOnSelect:false,hiddenDataCls:"x-hidden-display x-form-data-hidden",ariaRole:"combobox",childEls:{hiddenDataEl:true},filtered:false,afterRender:function(){var a=this;Ext.form.field.Picker.prototype.afterRender.apply(this,arguments);a.setHiddenValue(a.value)},multiSelect:false,triggerAction:"all",allQuery:"",queryParam:"query",queryMode:"remote",queryCaching:true,autoLoadOnValue:false,pageSize:0,anyMatch:false,caseSensitive:false,autoSelect:true,typeAhead:false,typeAheadDelay:250,selectOnTab:true,forceSelection:false,growToLongestValue:true,clearFilterOnBlur:true,defaultListConfig:{loadingHeight:70,minWidth:70,maxHeight:300,shadow:"sides"},transformInPlace:true,clearValueOnEmpty:true,getGrowWidth:function(){var d=this,g=d.inputEl.dom.value,e,h,j,c,b,k,a;if(d.growToLongestValue){e=d.displayField;h=d.store;j=h.data.length;c=0;for(b=0;bc){c=a;g=k}}}return g},initComponent:function(){var e=this,c=Ext.isDefined,b=e.store,d=e.transform,a,g;if("pinList" in e){e.collapseOnSelect=!e.pinList}if(d){a=Ext.getDom(d);if(a){if(!e.store){b=Ext.Array.map(Ext.Array.from(a.options),function(h){return[h.value,h.text]})}if(!e.name){e.name=a.name}if(!("value" in e)){e.value=a.value}}}e.bindStore(b||"ext-empty-store",true,true);g=e.queryMode==="local";if(!c(e.queryDelay)){e.queryDelay=g?10:500}if(!c(e.minChars)){e.minChars=g?0:4}Ext.form.field.Picker.prototype.initComponent.call(this);e.doQueryTask=new Ext.util.DelayedTask(e.doRawQuery,e);if(a){if(e.transformInPlace){e.render(a.parentNode,a);delete e.renderTo}Ext.removeNode(a)}},getSubTplData:function(b){var c,a;c=Ext.form.field.Picker.prototype.getSubTplData.call(this,b);a=c.inputElAriaAttributes;if(a){a["aria-autocomplete"]="list"}return c},getSubTplMarkup:function(c){var d=this,a="",b=Ext.form.field.Picker.prototype.getSubTplMarkup.apply(this,arguments);if(d.hiddenName){a=''}return a+b},applyDisplayTpl:function(b){var a=this;if(!b){b=new Ext.XTemplate('{[typeof values === "string" ? values : values["'+a.getDisplayField()+'"]]}'+a.getDelimiter()+"");b.auto=true}else{if(!b.isTemplate){b=new Ext.XTemplate(b)}}return b},applyFilters:function(b,c){var a=this;if(b===null||b.isFilterCollection){return b}if(b){if(!c){c=this.getFilters()}c.beginUpdate();c.splice(0,c.length,b);c.each(function(d){d.ownerId=a.id});c.endUpdate()}return c},applyValueNotFoundText:function(a){var b=this,c=b.valueNotFoundRecord||(b.valueNotFoundRecord=new Ext.data.Model());c.set(b.displayField,a);if(b.valueField&&b.displayField!==b.valueField){c.set(b.valueField,a)}return a},getFilters:function(b){var a=this.filters;if(!a&&b!==false){a=new Ext.util.FilterCollection();this.setFilters(a)}return a},updateFilters:function(a,b){var c=this;if(b){b.un("endupdate","onEndUpdateFilters",c)}if(a){a.on("endupdate","onEndUpdateFilters",c)}c.onEndUpdateFilters(a)},onEndUpdateFilters:function(e){var d=this,g=d.filtered,c=!!e&&(e.length>0),a,b;if(g||c){d.filtered=c;a=[];b=d.store.getFilters();b.each(function(h){if(h.ownerId===d.id&&!e.contains(h)){a.push(h)}});b.splice(0,a,e.items)}},completeEdit:function(c){var b=this,a=b.queryFilter;Ext.form.field.Picker.prototype.completeEdit.call(this,c);b.doQueryTask.cancel();b.assertValue();if(a&&b.queryMode==="local"&&b.clearFilterOnBlur){b.getStore().getFilters().remove(a)}},onFocus:function(b){var a=this;Ext.form.field.Picker.prototype.onFocus.call(this,b);if(a.triggerAction!=="all"&&a.queryFilter&&a.queryMode==="local"&&a.clearFilterOnBlur){delete a.lastQuery;a.doRawQuery()}},assertValue:function(){var b=this,c=b.getRawValue(),a=b.getDisplayValue(),d=b.lastSelectedRecords,e;if(b.forceSelection){if(b.multiSelect){if(c!==a){b.setRawValue(a)}}else{e=b.findRecordByDisplay(c);if(e){if(b.getDisplayValue([b.getRecordDisplayData(e)])!==a){b.select(e,true)}}else{if(d&&(!b.allowBlank||b.rawValue)){b.setValue(d)}else{if(d){delete b.lastSelectedRecords}b.setRawValue("")}}}}b.collapse()},onTypeAhead:function(){var e=this,d=e.displayField,b=e.store.findRecord(d,e.getRawValue()),c=e.getPicker(),g,a,h;if(b){g=b.get(d);a=g.length;h=e.getRawValue().length;c.highlightItem(c.getNode(b));if(h!==0&&h!==a){e.setRawValue(g);e.selectText(h,g.length)}}},resetToDefault:Ext.emptyFn,beforeReset:function(){var a=this.queryFilter;Ext.form.field.Picker.prototype.beforeReset.call(this);if(a){this.getStore().getFilters().remove(a)}},onUnbindStore:function(){var c=this,a=c.picker,b=c.queryFilter;if(b&&!c.store.destroyed){c.changingFilters=true;c.getStore().removeFilter(b,true);c.changingFilters=false}c.pickerSelectionModel.destroy();if(a){a.bindStore(null)}},onBindStore:function(a,c){var e=this,b=e.picker,d,g;if(a){if(a.autoCreated){e.queryMode="local";e.valueField=e.displayField="field1";if(!a.expanded){e.displayField="field2"}if(e.getDisplayTpl().auto){e.setDisplayTpl(null)}}if(!Ext.isDefined(e.valueField)){e.valueField=e.displayField}d={byValue:{rootProperty:"data",unique:false}};d.byValue.property=e.valueField;a.setExtraKeys(d);if(e.displayField===e.valueField){a.byText=a.byValue}else{d.byText={rootProperty:"data",unique:false};d.byText.property=e.displayField;a.setExtraKeys(d)}g={rootProperty:"data",extraKeys:{byInternalId:{property:"internalId"},byValue:{property:e.valueField,rootProperty:"data"}},listeners:{beginupdate:e.onValueCollectionBeginUpdate,endupdate:e.onValueCollectionEndUpdate,scope:e}};e.valueCollection=new Ext.util.Collection(g);e.pickerSelectionModel=new Ext.selection.DataViewModel({mode:e.multiSelect?"SIMPLE":"SINGLE",deselectOnContainerClick:false,enableInitialSelection:false,pruneRemoved:false,selected:e.valueCollection,store:a,listeners:{scope:e,lastselectedchanged:e.updateBindSelection}});if(!c){e.resetToDefault()}if(b){b.setSelectionModel(e.pickerSelectionModel);if(b.getStore()!==a){b.bindStore(a)}}}},bindStore:function(a,e,b){var d=this,c=d.queryFilter;d.mixins.storeholder.bindStore.call(d,a,b);a=d.getStore();if(a&&c&&!e){a.getFilters().add(c)}if(!b&&a&&!a.isEmptyStore){d.setValueOnData()}},getStoreListeners:function(b){if(!b.isEmptyStore){var c=this,a={datachanged:c.onDataChanged,load:c.onLoad,exception:c.onException,update:c.onStoreUpdate,remove:c.checkValueOnChange};if(!b.getRemoteFilter()){a.filterchange=c.checkValueOnChange}return a}},onDataChanged:function(){if(this.grow&&this.growToLongestValue){this.autoSize()}},checkValueOnChange:function(){var a=this;if(!a.destroying&&a.getStore().isLoaded()){if(a.multiSelect){}else{if(a.forceSelection&&!a.changingFilters&&!a.findRecordByValue(a.value)){a.setValue(null)}}}},onStoreUpdate:function(b,a){this.updateValue()},onException:function(){this.collapse()},onLoad:function(c,b,e){var d=this,a=!d.valueCollection.byValue.get(d.value);if(e&&a&&!(c.lastOptions&&"rawQuery" in c.lastOptions)){d.setValueOnData()}d.checkValueOnChange()},setValueOnData:function(){var a=this;a.setValue(a.value);if(a.isExpanded&&a.getStore().getCount()){a.doAutoSelect()}},doRawQuery:function(){var a=this,b=a.inputEl.dom.value;if(a.multiSelect){b=b.split(a.delimiter).pop()}a.doQuery(b,false,true)},doQuery:function(h,c,g){var d=this,a=d.getStore(),e=a.filters&&!a.filters.length&&!!h,b=d.beforeQuery({query:h||"",rawQuery:g,forceAll:c,combo:d,cancel:false});if(b!==false&&!b.cancel){if(d.queryCaching&&!e&&b.query===d.lastQuery){d.getPicker().refresh();d.expand()}else{d.lastQuery=b.query;if(d.queryMode==="local"){d.doLocalQuery(b)}else{d.doRemoteQuery(b)}}}return true},beforeQuery:function(a){var b=this;if(b.fireEvent("beforequery",a)===false){a.cancel=true}else{if(!a.cancel){if(a.query.length0){a=c.picker.getSelectionModel();if(a.lastSelected&&a.selected.length){d=a.lastSelected}b.getNavigationModel().setPosition(d)}},doTypeAhead:function(){var b=this,a=Ext.event.Event;if(!b.typeAheadTask){b.typeAheadTask=new Ext.util.DelayedTask(b.onTypeAhead,b)}if(b.lastKey!==a.BACKSPACE&&b.lastKey!==a.DELETE){b.typeAheadTask.delay(b.typeAheadDelay)}},onTriggerClick:function(){var a=this;if(!a.readOnly&&!a.disabled){if(a.isExpanded){a.collapse()}else{if(a.triggerAction==="all"){a.doQuery(a.allQuery,true)}else{if(a.triggerAction==="last"){a.doQuery(a.lastQuery,true)}else{a.doQuery(a.getRawValue(),false,true)}}}}},onFieldMutation:function(h){var d=this,b=h.getKey(),c=b===h.BACKSPACE||b===h.DELETE,g=d.inputEl.dom.value,a=g.length;if(!d.readOnly&&(g!==d.lastMutatedValue||c)&&b!==h.TAB){d.lastMutatedValue=g;d.lastKey=b;if(a&&(h.type!=="keyup"||(!h.isSpecialKey()||c))){d.doQueryTask.delay(d.queryDelay)}else{if(!a&&(!b||c)){++d.suspendCheckChange;if(!d.multiSelect){d.value=null;d.displayTplData=undefined}if(d.clearValueOnEmpty){d.valueCollection.beginUpdate();d.pickerSelectionModel.deselectAll();d.valueCollection.removeAll();d.valueCollection.endUpdate()}d.collapse();if(d.queryFilter){d.changingFilters=true;d.store.removeFilter(d.queryFilter,true);d.changingFilters=false}--d.suspendCheckChange}Ext.form.field.Picker.prototype.onFieldMutation.call(this,h)}}},onDestroy:function(){var a=this;a.doQueryTask.cancel();if(a.typeAheadTask){a.typeAheadTask.cancel();a.typeAheadTask=null}a.bindStore(null);a.valueCollection=Ext.destroy(a.valueCollection);Ext.form.field.Picker.prototype.onDestroy.call(this)},onAdded:function(){var a=this;Ext.form.field.Picker.prototype.onAdded.apply(this,arguments);if(a.picker){a.picker.ownerCt=a.up("[floating]");a.picker.registerWithOwnerCt()}},createPicker:function(){var c=this,b,a=Ext.apply({xtype:"boundlist",id:c.pickerId,pickerField:c,selectionModel:c.pickerSelectionModel,floating:true,hidden:true,store:c.getPickerStore(),displayField:c.displayField,preserveScrollOnRefresh:true,pageSize:c.pageSize,tpl:c.tpl},c.listConfig,c.defaultListConfig);b=c.picker=Ext.widget(a);if(c.pageSize){b.pagingToolbar.on("beforechange",c.onPageChange,c)}if(!b.initialConfig.maxHeight){b.on({beforeshow:c.onBeforePickerShow,scope:c})}b.getSelectionModel().on({beforeselect:c.onBeforeSelect,beforedeselect:c.onBeforeDeselect,focuschange:c.onFocusChange,scope:c});b.getNavigationModel().navigateOnSpace=false;return b},getPickerStore:function(){return this.store},onBeforePickerShow:function(a){var b=this,d=b.getPosition()[1]-Ext.getBody().getScroll().top,c=Ext.Element.getViewportHeight()-d-b.getHeight();a.maxHeight=Math.max(d,c)-5},onBeforeSelect:function(c,a,b){return this.fireEvent("beforeselect",this,a,b)},onBeforeDeselect:function(c,a,b){return this.fireEvent("beforedeselect",this,a,b)},onFocusChange:function(c,a,b){var d=this.picker,e;if(b){e=Ext.get(d.getNodeByRecord(b));if(e){this.ariaEl.dom.setAttribute("aria-activedescendant",e.id)}}},getSelection:function(){var a=this.getPicker().getSelectionModel(),b=a.getSelection();return b.length?a.getLastSelected():null},updateSelection:function(a){var b=this,c;if(!b.ignoreNextSelection){b.ignoreNextSelection=true;c=b.getPicker().getSelectionModel();if(a){c.select(a);b.hasHadSelection=true}else{c.deselectAll()}b.ignoreNextSelection=false}},updateBindSelection:function(a,c){var d=this,b=null;if(!d.ignoreNextSelection){d.ignoreNextSelection=true;if(c.length){b=a.getLastSelected();d.hasHadSelection=true}if(d.hasHadSelection){d.setSelection(b)}d.ignoreNextSelection=false}},onValueCollectionBeginUpdate:Ext.emptyFn,onValueCollectionEndUpdate:function(){var d=this,c=d.store,e=d.valueCollection.getRange(),b=e[0],a=e.length;d.updateBindSelection(d.pickerSelectionModel,e);if(d.isSelectionUpdating()){return}Ext.suspendLayouts();d.lastSelection=e;if(a){d.lastSelectedRecords=e}d.updateValue();if(a&&((!d.multiSelect&&c.contains(b))||d.collapseOnSelect||!c.getCount())){d.updatingValue=true;d.collapse();d.updatingValue=false}Ext.resumeLayouts(true);if(a&&!d.suspendCheckChange){if(!d.multiSelect){e=b}d.fireEvent("select",d,e)}},isSelectionUpdating:function(){var a=this.pickerSelectionModel;return a.deselectingDuringSelect||a.refreshing},onExpand:function(){var a=this.getPicker().getNavigationModel();if(a){a.enable()}this.doAutoSelect()},onCollapse:function(){var a=this.getPicker().getNavigationModel();if(a){a.disable()}if(this.updatingValue){this.doQueryTask.cancel()}},select:function(d,a){var c=this,b=c.picker,e;if(d&&d.isModel&&a===true&&b){e=!b.getSelectionModel().isSelected(d)}if(!e){c.suspendEvent("select")}c.setValue(d);c.resumeEvent("select")},findRecord:function(d,c){var b=this.store,a=b.findExact(d,c);return a!==-1?b.getAt(a):false},getSelectedRecord:function(){return this.findRecordByValue(this.value)||null},findRecordByValue:function(c){var a=this.store.byValue.get(c),b=false;if(a){b=a[0]||a}return b},findRecordByDisplay:function(c){var a=this.store.byText.get(c),b=false;if(a){b=a[0]||a}return b},addValue:function(a){if(a!=null){return this.doSetValue(a,true)}},setValue:function(b){var a=this;if(b!=null){return a.doSetValue(b)}else{a.suspendEvent("select");a.valueCollection.beginUpdate();a.pickerSelectionModel.deselectAll();a.valueCollection.endUpdate();a.lastSelectedRecords=null;a.resumeEvent("select")}},setRawValue:function(a){Ext.form.field.Picker.prototype.setRawValue.call(this,a);this.lastMutatedValue=a},doSetValue:function(q,m){var w=this,h=w.getStore(),j=h.getModel(),t=[],g=[],s=w.autoLoadOnValue,e=h.getCount()>0||h.isLoaded(),a=h.hasPendingLoad(),n=s&&!e&&!a,l=w.forceSelection,p=w.pickerSelectionModel,b=w.displayField===w.valueField,o=h.isEmptyStore,k=w.lastSelection,u,v,c,r,d,x;if(a||n||!e||o){if(!q.isModel){if(m){w.value=Ext.Array.from(w.value).concat(q)}else{w.value=q}w.setHiddenValue(w.value);w.setRawValue(b?q:"")}if(n&&!o){h.load()}if(!q.isModel||o){return w}}q=m?Ext.Array.from(w.value).concat(q):Ext.Array.from(q);for(u=0,v=q.length;u0){e.hiddenDataEl.setHtml(Ext.DomHelper.markup({tag:"input",type:"hidden",name:a}));c=1;h=b.firstChild}while(c>g){b.removeChild(k[0]);--c}while(c','
    ','','
    ','{.}',"
    ","
    ","
    ",'
    ','
    ','
    ','',"
    ",'
    ','',"
    ","
    ",'','
    ','{.}',"
    ","
    ","
    ",'
    ','','
    {%',"var me=values.$comp, okBtn=me.okBtn, cancelBtn=me.cancelBtn;","okBtn.ownerLayout = cancelBtn.ownerLayout = me.componentLayout;","okBtn.ownerCt = cancelBtn.ownerCt = me;","Ext.DomHelper.generateMarkup(okBtn.getRenderTree(), out);","Ext.DomHelper.generateMarkup(cancelBtn.getRenderTree(), out);","%}
    ","
    ",""],okText:"OK",cancelText:"Cancel",baseCls:"x-monthpicker",showButtons:true,footerButtonUI:"default",measureWidth:35,measureMaxHeight:20,smallCls:"x-monthpicker-small",totalYears:10,yearOffset:5,monthOffset:6,initComponent:function(){var a=this;a.selectedCls=a.baseCls+"-selected";if(a.small){a.addCls(a.smallCls)}a.setValue(a.value);a.activeYear=a.getYear(new Date().getFullYear()-4,-4);if(a.showButtons){a.okBtn=new Ext.button.Button({ui:a.footerButtonUI,text:a.okText,handler:a.onOkClick,scope:a});a.cancelBtn=new Ext.button.Button({ui:a.footerButtonUI,text:a.cancelText,handler:a.onCancelClick,scope:a})}Ext.Component.prototype.initComponent.call(this)},beforeRender:function(){var g=this,c=0,b=[],a=Ext.Date.getShortMonthName,e=g.monthOffset,h=g.monthMargin,d="";if(g.padding&&!g.width){g.cacheWidth()}Ext.Component.prototype.beforeRender.call(this);for(;cc.measureMaxHeight){--b;a.setStyle("margin","0 "+b+"px")}return b},getLargest:function(a){var b=0;this.months.each(function(d){var c=d.getHeight();if(c>b){b=c}});return b},setValue:function(c){var b=this,d=b.activeYear,a;if(!c){b.value=[null,null]}else{if(Ext.isDate(c)){b.value=[c.getMonth(),c.getFullYear()]}else{b.value=[c[0],c[1]]}}if(b.rendered){a=b.value[1];if(a!==null){if((ad+b.yearOffset)){b.activeYear=a-b.yearOffset+1}}b.updateBody()}return b},getValue:function(){return this.value},hasSelection:function(){var a=this.value;return a[0]!==null&&a[1]!==null},getYears:function(){var d=this,e=d.yearOffset,g=d.activeYear,a=g+e,c=g,b=[];for(;c','
    ','','
    {%this.renderMonthBtn(values, out)%}
    ','',"
    ",'',"",'','','","","","","",'','',"{#:this.isEndOfWeek}",'","","","","
    ','',"
    ','
    ',"
    ",'','',"",'
    {todayText}.
    ','
    {ariaMinText}.
    ','
    {ariaMaxText}.
    ','
    {ariaDisabledDaysText}.
    ','
    {ariaDisabledDatesText}.
    ',"",{firstInitial:function(a){return Ext.picker.Date.prototype.getDayInitial(a)},isEndOfWeek:function(b){b--;var a=b%7===0&&b!==0;return a?'':""},renderTodayBtn:function(a,b){Ext.DomHelper.generateMarkup(a.$comp.todayBtn.getRenderTree(),b)},renderMonthBtn:function(a,b){Ext.DomHelper.generateMarkup(a.$comp.monthBtn.getRenderTree(),b)}}],initHour:12,numDays:42,initComponent:function(){var b=this,a=Ext.Date.clearTime;b.selectedCls=b.baseCls+"-selected";b.disabledCellCls=b.baseCls+"-disabled";b.prevCls=b.baseCls+"-prevday";b.activeCls=b.baseCls+"-active";b.cellCls=b.baseCls+"-cell";b.nextCls=b.baseCls+"-prevday";b.todayCls=b.baseCls+"-today";if(!b.format){b.format=Ext.Date.defaultFormat}if(!b.dayNames){b.dayNames=Ext.Date.dayNames}b.dayNames=b.dayNames.slice(b.startDay).concat(b.dayNames.slice(0,b.startDay));Ext.Component.prototype.initComponent.call(this);b.value=b.value?a(b.value,true):a(new Date());b.initDisabledDays()},getRefOwner:function(){return this.pickerField||Ext.Component.prototype.getRefOwner.call(this)},getRefItems:function(){var a=[],c=this.monthBtn,b=this.todayBtn;if(c){a.push(c)}if(b){a.push(b)}return a},beforeRender:function(){var c=this,b=Ext.String.htmlEncode,d=new Array(c.numDays),a=Ext.Date.format(new Date(),c.format);if(c.padding&&!c.width){c.cacheWidth()}c.monthBtn=new Ext.button.Split({ownerCt:c,ownerLayout:c.getComponentLayout(),text:"",tooltip:c.monthYearText,tabIndex:-1,ariaRole:"presentation",listeners:{click:c.doShowMonthPicker,arrowclick:c.doShowMonthPicker,scope:c}});if(c.showToday){c.todayBtn=new Ext.button.Button({ui:c.footerButtonUI,ownerCt:c,ownerLayout:c.getComponentLayout(),text:Ext.String.format(c.todayText,a),tooltip:Ext.String.format(c.todayTip,a),tooltipType:"title",tabIndex:-1,ariaRole:"presentation",handler:c.selectToday,scope:c})}Ext.Component.prototype.beforeRender.call(this);Ext.applyIf(c,{renderData:{}});Ext.apply(c.renderData,{dayNames:c.dayNames,showToday:c.showToday,prevText:b(c.prevText),nextText:b(c.nextText),todayText:b(c.todayText),ariaMinText:b(c.ariaMinText),ariaMaxText:b(c.ariaMaxText),ariaDisabledDaysText:b(c.ariaDisabledDaysText),ariaDisabledDatesText:b(c.ariaDisabledDatesText),days:d});c.protoEl.unselectable()},cacheWidth:function(){var a=this,b=a.parseBox(a.padding),c=Ext.getBody().createChild({cls:a.baseCls+" "+a.borderBoxCls,style:"position:absolute;top:-1000px;left:-1000px;"});a.self.prototype.width=c.getWidth()+b.left+b.right;c.destroy()},onRender:function(b,a){var c=this;Ext.Component.prototype.onRender.apply(this,arguments);c.cells=c.eventEl.select("tbody td");c.textNodes=c.eventEl.query("tbody td div");c.eventEl.set({"aria-labelledby":c.monthBtn.id});c.mon(c.eventEl,{scope:c,mousewheel:c.handleMouseWheel,click:{fn:c.handleDateClick,delegate:"div."+c.baseCls+"-date"}})},initEvents:function(){var c=this,d=c.pickerField,a=Ext.Date,b=a.DAY;Ext.Component.prototype.initEvents.call(this);if(d){c.el.on("mousedown",c.onMouseDown,c)}c.monthBtn.el.on("mousedown",c.onMouseDown,c);c.prevRepeater=new Ext.util.ClickRepeater(c.prevEl,{handler:c.showPrevMonth,scope:c,mousedownStopEvent:true});c.nextRepeater=new Ext.util.ClickRepeater(c.nextEl,{handler:c.showNextMonth,scope:c,mousedownStopEvent:true});c.keyNav=new Ext.util.KeyNav(c.eventEl,Ext.apply({scope:c,left:function(g){if(g.ctrlKey){g.preventDefault();c.showPrevMonth()}else{c.update(a.add(c.activeDate,b,-1))}},right:function(g){if(g.ctrlKey){g.preventDefault();c.showNextMonth()}else{c.update(a.add(c.activeDate,b,1))}},up:function(g){if(g.ctrlKey){c.showNextYear()}else{c.update(a.add(c.activeDate,b,-7))}},down:function(g){if(g.ctrlKey){c.showPrevYear()}else{c.update(a.add(c.activeDate,b,7))}},pageUp:function(g){if(g.ctrlKey){c.showPrevYear()}else{c.showPrevMonth()}},pageDown:function(g){if(g.ctrlKey){c.showNextYear()}else{c.showNextMonth()}},tab:function(g){c.handleTabKey(g);return true},enter:function(g){c.handleDateClick(g,c.activeCell.firstChild)},space:function(){c.setValue(new Date(c.activeCell.firstChild.dateValue));var e=c.startValue,g=c.value,h;if(d){h=d.getValue();if(h&&e&&h.getTime()===g.getTime()){d.setValue(e)}else{d.setValue(g)}}},home:function(g){c.update(a.getFirstDateOfMonth(c.activeDate))},end:function(g){c.update(a.getLastDateOfMonth(c.activeDate))}},c.keyNavConfig));if(c.disabled){c.syncDisabled(true)}c.update(c.value)},onMouseDown:function(a){a.preventDefault()},handleTabKey:function(d){var c=this,a=c.getSelectedDate(c.activeDate),b=c.handler;if(!c.disabled&&a.dateValue&&!Ext.fly(a.parentNode).hasCls(c.disabledCellCls)){c.setValue(new Date(a.dateValue));c.fireEvent("select",c,c.value);if(b){b.call(c.scope||c,c,c.value)}c.onSelect()}else{c.fireEventArgs("tabout",[c])}},getSelectedDate:function(a){var d=this,i=a.getTime(),j=d.cells,k=d.selectedCls,g=j.elements,e=g.length,h,b;j.removeCls(k);for(b=0;b0){this.showPrevMonth()}else{if(b<0){this.showNextMonth()}}}},handleDateClick:function(d,a){var c=this,b=c.handler;d.stopEvent();if(!c.disabled&&a.dateValue&&!Ext.fly(a.parentNode).hasCls(c.disabledCellCls)){c.setValue(new Date(a.dateValue));c.fireEvent("select",c,c.value);if(b){b.call(c.scope||c,c,c.value)}c.onSelect()}},onSelect:function(){if(this.hideOnSelect){this.hide()}},selectToday:function(){var c=this,a=c.todayBtn,b=c.handler;if(a&&!a.disabled){c.setValue(Ext.Date.clearTime(new Date()));c.fireEvent("select",c,c.value);if(b){b.call(c.scope||c,c,c.value)}c.onSelect()}return c},selectedUpdate:function(e){var h=this,g=e.getTime(),d=h.cells,b=h.selectedCls,j,i=d.getCount(),a;h.eventEl.dom.setAttribute("aria-busy","true");a=h.activeCell;if(a){Ext.fly(a).removeCls(b);a.setAttribute("aria-selected",false)}for(j=0;ju||(B&&w&&B.test(m.dateFormat(E,w)))||(G&&G.indexOf(E.getDay())!==-1));if(!D.disabled){D.todayBtn.setDisabled(a)}}l=function(I,H){var i=d[I],J=[];if(!i.hasAttribute("id")){i.setAttribute("id",D.id+"-cell-"+I)}r=+m.clearTime(q,true);i.firstChild.dateValue=r;i.setAttribute("aria-label",m.format(q,o));i.removeAttribute("aria-describedby");i.removeAttribute("data-qtip");if(r===y){H+=" "+D.todayCls;J.push(D.id+"-todayText")}if(r===k){D.activeCell=i;D.eventEl.dom.setAttribute("aria-activedescendant",i.id);i.setAttribute("aria-selected",true);H+=" "+D.selectedCls;D.fireEvent("highlightitem",D,i)}else{i.setAttribute("aria-selected",false)}if(ru){H+=" "+F;J.push(D.id+"-ariaMaxText");i.setAttribute("data-qtip",D.maxText)}else{if(G&&G.indexOf(q.getDay())!==-1){i.setAttribute("data-qtip",A);J.push(D.id+"-ariaDisabledDaysText");H+=" "+F}else{if(B&&w){g=m.dateFormat(q,w);if(B.test(g)){i.setAttribute("data-qtip",s.replace("%0",g));J.push(D.id+"-ariaDisabledDatesText");H+=" "+F}}}}}if(J.length){i.setAttribute("aria-describedby",J.join(" "))}i.className=H+" "+D.cellCls};D.eventEl.dom.setAttribute("aria-busy","true");for(;v=j){n=(++C);b=D.nextCls}else{n=v-e+1;b=D.activeCls}}c[v].innerHTML=n;q.setDate(q.getDate()+1);l(v,b)}D.eventEl.dom.removeAttribute("aria-busy");D.monthBtn.setText(Ext.Date.format(z,D.monthYearFormat))},update:function(a,d){var b=this,c=b.activeDate;if(b.rendered){b.activeDate=a;if(!d&&c&&b.el&&c.getMonth()===a.getMonth()&&c.getFullYear()===a.getFullYear()){b.selectedUpdate(a,c)}else{b.fullUpdate(a,c)}}return b},beforeDestroy:function(){var a=this;if(a.rendered){Ext.destroy(a.keyNav,a.monthPicker,a.monthBtn,a.nextRepeater,a.prevRepeater,a.todayBtn,a.todayElSpan);delete a.textNodes;delete a.cells.elements}Ext.Component.prototype.beforeDestroy.call(this)},privates:{finishRenderChildren:function(){var a=this;Ext.Component.prototype.finishRenderChildren.call(this);a.monthBtn.finishRender();if(a.showToday){a.todayBtn.finishRender()}},getFocusEl:function(){return this.eventEl},syncDisabled:function(b){var c=this,a=c.keyNav;if(a){a.setDisabled(b);c.prevRepeater.setDisabled(b);c.nextRepeater.setDisabled(b);if(c.todayBtn){c.todayBtn.setDisabled(b)}}}}},0,["datepicker"],["component","box","datepicker"],{component:true,box:true,datepicker:true},["widget.datepicker"],0,[Ext.picker,"Date",Ext,"DatePicker"],0));(Ext.cmd.derive("Ext.form.field.Date",Ext.form.field.Picker,{alternateClassName:["Ext.form.DateField","Ext.form.Date"],format:"m/d/Y",ariaFormat:"M j Y",altFormats:"m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d|n-j|n/j",disabledDaysText:"Disabled",ariaDisabledDaysText:"This day of week is disabled",disabledDatesText:"Disabled",ariaDisabledDatesText:"This date cannot be selected",minText:"The date in this field must be equal to or after {0}",ariaMinText:"The date must be equal to or after {0}",maxText:"The date in this field must be equal to or before {0}",ariaMaxText:"The date must be equal to or before {0}",invalidText:"{0} is not a valid date - it must be in the format {1}",formatText:"Expected date format: {0}",triggerCls:"x-form-date-trigger",showToday:true,useStrict:undefined,initTime:"12",initTimeFormat:"H",matchFieldWidth:false,startDay:0,valuePublishEvent:["select","blur"],ariaRole:"combobox",initComponent:function(){var d=this,b=Ext.isString,c,a;c=d.minValue;a=d.maxValue;if(b(c)){d.minValue=d.parseDate(c)}if(b(a)){d.maxValue=d.parseDate(a)}d.disabledDatesRE=null;d.initDisabledDays();Ext.form.field.Picker.prototype.initComponent.call(this)},initValue:function(){var a=this,b=a.value;if(Ext.isString(b)){a.value=a.rawToValue(b)}Ext.form.field.Picker.prototype.initValue.call(this)},initDisabledDays:function(){if(this.disabledDates){var b=this.disabledDates,a=b.length-1,g="(?:",h,e=b.length,c;for(h=0;h0?q:this.formatDate(this.processRawValue(this.getRawValue()));var j=this,p=Ext.String.format,k=Ext.Date.clearTime,o=Ext.form.field.Picker.prototype.getErrors.call(this,q),n=j.disabledDays,d=j.disabledDatesRE,m=j.minValue,h=j.maxValue,g=n?n.length:0,e=0,a,b,l,c;if(q===null||q.length<1){return o}a=q;q=j.parseDate(q);if(!q){o.push(p(j.invalidText,a,Ext.Date.unescapeFormat(j.format)));return o}c=q.getTime();if(m&&ck(h).getTime()){o.push(p(j.maxText,j.formatDate(h)))}if(n){l=q.getDay();for(;e style="{fieldStyle}"',' class="{fieldCls} {fieldCls}-{ui}">{value}',{compiled:true,disableFormats:true}],ariaRole:undefined,focusable:false,readOnly:true,fieldCls:"x-form-display-field",fieldBodyCls:"x-form-display-field-body",htmlEncode:false,noWrap:false,validateOnChange:false,initEvents:Ext.emptyFn,submitValue:false,getValue:function(){return this.value},valueToRaw:function(a){if(a||a===0||a===false){return a}else{return""}},isDirty:function(){return false},isValid:Ext.returnTrue,validate:Ext.returnTrue,getRawValue:function(){return this.rawValue},setRawValue:function(b){var a=this;b=Ext.valueFrom(b,"");a.rawValue=b;if(a.rendered){a.inputEl.dom.innerHTML=a.getDisplayValue();a.updateLayout()}return b},getDisplayValue:function(){var a=this,b=this.getRawValue(),c;if(a.renderer){c=a.renderer.call(a.scope||a,b,a)}else{c=a.htmlEncode?Ext.util.Format.htmlEncode(b):b}return c},getSubTplData:function(b){var a=Ext.form.field.Base.prototype.getSubTplData.apply(this,arguments);a.value=this.getDisplayValue();return a}},0,["displayfield"],["component","box","field","displayfield"],{component:true,box:true,field:true,displayfield:true},["widget.displayfield"],0,[Ext.form.field,"Display",Ext.form,"DisplayField",Ext.form,"Display"],0));(Ext.cmd.derive("Ext.form.field.FileButton",Ext.button.Button,{childEls:["fileInputEl"],inputCls:"x-form-file-input",cls:"x-form-file-btn",preventDefault:false,tabIndex:undefined,autoEl:{tag:"div",unselectable:"on"},afterTpl:['tabindex="{tabIndex}"',">"],keyHandlers:null,ariaEl:"fileInputEl",getAfterMarkup:function(a){return this.getTpl("afterTpl").apply(a)},getTemplateArgs:function(){var b=this,a;a=Ext.button.Button.prototype.getTemplateArgs.call(this);a.inputCls=b.inputCls;a.inputName=b.inputName||b.id;a.tabIndex=b.tabIndex||null;a.role=b.ariaRole;return a},afterRender:function(){var a=this;Ext.button.Button.prototype.afterRender.apply(this,arguments);a.fileInputEl.on({scope:a,change:a.fireChange,focus:a.onFileFocus,blur:a.onFileBlur})},fireChange:function(a){this.fireEvent("change",this,a,this.fileInputEl.dom.value)},createFileInput:function(b){var c=this,a=c.fileInputEl=c.el.createChild({name:c.inputName,id:!b?c.id+"-fileInputEl":undefined,cls:c.inputCls,tag:"input",type:"file",size:1,role:"button"});a.dom.setAttribute("data-componentid",c.id);a.on({scope:c,change:c.fireChange,focus:c.onFileFocus,blur:c.onFileBlur})},onFileFocus:function(b){var a=this.ownerCt;if(!this.hasFocus){this.onFocus(b)}if(a&&!a.hasFocus){a.onFocus(b)}},onFileBlur:function(b){var a=this.ownerCt;if(this.hasFocus){this.onBlur(b)}if(a&&a.hasFocus){a.onBlur(b)}},reset:function(a){var b=this;if(a){b.fileInputEl.destroy()}b.createFileInput(!a)},restoreInput:function(a){var b=this;b.fileInputEl.destroy();a=Ext.get(a);b.el.appendChild(a);b.fileInputEl=a},onDisable:function(){Ext.button.Button.prototype.onDisable.call(this);this.fileInputEl.dom.disabled=true},onEnable:function(){Ext.button.Button.prototype.onEnable.call(this);this.fileInputEl.dom.disabled=false},privates:{getFocusEl:function(){return this.fileInputEl},getFocusClsEl:function(){return this.el}}},0,["filebutton"],["component","box","button","filebutton"],{component:true,box:true,button:true,filebutton:true},["widget.filebutton"],0,[Ext.form.field,"FileButton"],0));(Ext.cmd.derive("Ext.form.trigger.Component",Ext.form.trigger.Trigger,{cls:"x-form-trigger-cmp",onFieldRender:function(){var b=this,a=b.component;Ext.form.trigger.Trigger.prototype.onFieldRender.call(this);if(!a.isComponent&&!a.isWidget){a=Ext.widget(a)}b.component=a;a.render(b.el)},destroy:function(){var a=this.component;if(a.isComponent||a.isWidget){a.destroy()}this.component=null;Ext.form.trigger.Trigger.prototype.destroy.call(this)}},0,0,0,0,["trigger.component"],0,[Ext.form.trigger,"Component"],0));(Ext.cmd.derive("Ext.form.field.File",Ext.form.field.Text,{alternateClassName:["Ext.form.FileUploadField","Ext.ux.form.FileUploadField","Ext.form.File"],emptyText:undefined,needArrowKeys:false,triggers:{filebutton:{type:"component",hideOnReadOnly:false,preventMouseDown:false}},buttonText:"Browse...",buttonOnly:false,buttonMargin:3,clearOnSubmit:true,extraFieldBodyCls:"x-form-file-wrap",inputCls:"x-form-text-file",readOnly:true,editable:false,submitValue:false,triggerNoEditCls:"",childEls:["browseButtonWrap"],applyTriggers:function(a){var b=this,c=(a||{}).filebutton;if(c){c.component=Ext.apply({xtype:"filebutton",ownerCt:b,id:b.id+"-button",ui:b.ui,disabled:b.disabled,text:b.buttonText,style:b.buttonOnly?"":b.getButtonMarginProp()+b.buttonMargin+"px",inputName:b.getName(),listeners:{scope:b,change:b.onFileChange}},b.buttonConfig);return Ext.form.field.Text.prototype.applyTriggers.call(this,a)}},getSubTplData:function(a){var b=Ext.form.field.Text.prototype.getSubTplData.call(this,a);b.tabIdx=-1;return b},onRender:function(){var d=this,e,c,a,b;(arguments.callee.$previous||Ext.form.field.Text.prototype.onRender).apply(this,arguments);e=d.inputEl;e.dom.name="";e.on("focus",d.onInputFocus,d);e.on("mousedown",d.onInputMouseDown,d);b=d.getTrigger("filebutton");c=d.button=b.component;d.fileInputEl=c.fileInputEl;a=c.el;if(d.buttonOnly){d.inputWrap.setDisplayed(false);d.shrinkWrap=3}b.el.setWidth(a.getWidth()+a.getMargin("lr"));if(Ext.isIE){d.button.getEl().repaint()}},getTriggerMarkup:function(){return''},onFileChange:function(a,c,b){this.duringFileSelect=true;Ext.form.field.File.superclass.setValue.call(this,b);delete this.duringFileSelect},didValueChange:function(){return !!this.duringFileSelect},setEmptyText:Ext.emptyFn,setValue:Ext.emptyFn,reset:function(){var b=this,a=b.clearOnSubmit;if(b.rendered){b.button.reset(a);b.fileInputEl=b.button.fileInputEl;if(a){b.inputEl.dom.value="";Ext.form.field.File.superclass.setValue.call(this,null)}}Ext.form.field.Text.prototype.reset.call(this)},onShow:function(){Ext.form.field.Text.prototype.onShow.call(this);this.button.updateLayout()},onDisable:function(){Ext.form.field.Text.prototype.onDisable.call(this);this.button.disable()},onEnable:function(){Ext.form.field.Text.prototype.onEnable.call(this);this.button.enable()},isFileUpload:Ext.returnTrue,extractFileInput:function(){var b=this,a;if(b.rendered){a=b.button.fileInputEl.dom;b.reset()}else{a=document.createElement("input");a.type="file";a.className="x-hidden-display";a.name=b.getName()}return a},restoreInput:function(b){if(this.rendered){var a=this.button;a.restoreInput(b);this.fileInputEl=a.fileInputEl}},onDestroy:function(){this.fileInputEl=this.button=null;Ext.form.field.Text.prototype.onDestroy.call(this)},getButtonMarginProp:function(){return"margin-left:"},onInputFocus:function(a){this.focus();if(Ext.isIE9m){this.fileInputEl.addCls("x-position-relative");this.fileInputEl.removeCls("x-position-relative")}},onInputMouseDown:function(a){a.preventDefault();this.focus()},privates:{getFocusEl:function(){return this.button},getFocusClsEl:Ext.privateFn}},0,["fileuploadfield","filefield"],["component","box","field","textfield","filefield","fileuploadfield"],{component:true,box:true,field:true,textfield:true,filefield:true,fileuploadfield:true},["widget.filefield","widget.fileuploadfield"],0,[Ext.form.field,"File",Ext.form,"FileUploadField",Ext.ux.form,"FileUploadField",Ext.form,"File"],0));(Ext.cmd.derive("Ext.form.field.Hidden",Ext.form.field.Base,{alternateClassName:"Ext.form.Hidden",focusable:false,inputType:"hidden",isTextInput:false,hideLabel:true,hidden:true,ariaRole:"presentation",initComponent:function(){this.formItemCls+="-hidden";Ext.form.field.Base.prototype.initComponent.call(this)},isEqual:function(b,a){return this.isEqualAsString(b,a)},initEvents:Ext.emptyFn,setSize:Ext.emptyFn,setWidth:Ext.emptyFn,setHeight:Ext.emptyFn,setPosition:Ext.emptyFn,setPagePosition:Ext.emptyFn,markInvalid:Ext.emptyFn,clearInvalid:Ext.emptyFn},0,["hiddenfield","hidden"],["component","box","field","hiddenfield","hidden"],{component:true,box:true,field:true,hiddenfield:true,hidden:true},["widget.hidden","widget.hiddenfield"],0,[Ext.form.field,"Hidden",Ext.form,"Hidden"],0));(Ext.cmd.derive("Ext.tip.Tip",Ext.panel.Panel,{alternateClassName:"Ext.Tip",minWidth:40,maxWidth:500,shadow:"sides",defaultAlign:"tl-bl?",constrainPosition:true,autoRender:true,hidden:true,baseCls:"x-tip",focusOnToFront:false,maskOnDisable:false,closeAction:"hide",alwaysFramed:true,frameHeader:false,initComponent:function(){var a=this;a.floating=Ext.apply({},{shadow:a.shadow,constrain:a.constrainPosition},a.self.prototype.floating);Ext.panel.Panel.prototype.initComponent.apply(this,arguments);a.constrain=a.constrain||a.constrainPosition},showAt:function(b){var a=this;Ext.panel.Panel.prototype.showAt.apply(this,arguments);if(a.isVisible()){a.setPagePosition(b[0],b[1]);if(a.constrainPosition||a.constrain){a.doConstrain()}a.toFront(true)}},privates:{initDraggable:function(){var a=this;a.draggable={el:a.getDragEl(),delegate:a.header.el,constrain:a,constrainTo:a.el.dom.parentNode};Ext.Component.prototype.initDraggable.call(a)}},ghost:undefined,unghost:undefined},0,["tip"],["component","box","container","panel","tip"],{component:true,box:true,container:true,panel:true,tip:true},["widget.tip"],0,[Ext.tip,"Tip",Ext,"Tip"],0));(Ext.cmd.derive("Ext.tip.ToolTip",Ext.tip.Tip,{alternateClassName:"Ext.ToolTip",autoHide:true,showDelay:500,hideDelay:200,dismissDelay:5000,trackMouse:false,anchorToTarget:true,anchorOffset:0,targetCounter:0,quickShowInterval:250,hideAction:"hide",fadeOutDuration:1000,ariaRole:"tooltip",initComponent:function(){var a=this;Ext.tip.Tip.prototype.initComponent.apply(this,arguments);a.lastActive=new Date();a.setTarget(a.target);a.origAnchor=a.anchor},onRender:function(b,a){var c=this;Ext.tip.Tip.prototype.onRender.apply(this,arguments);c.anchorCls="x-tip-anchor-"+c.getAnchorPosition();c.anchorEl=c.el.createChild({role:"presentation",cls:"x-tip-anchor "+c.anchorCls})},setTarget:function(d){var b=this,a=Ext.get(d),c;if(b.target){c=Ext.get(b.target);if(Ext.supports.Touch){b.mun(c,"tap",b.onTargetOver,b)}else{b.mun(c,{mouseover:b.onTargetOver,mouseout:b.onTargetOut,mousemove:b.onMouseMove,scope:b})}}b.target=a;if(a){if(Ext.supports.Touch){b.mon(a,{tap:b.onTargetOver,scope:b})}else{b.mon(a,{mouseover:b.onTargetOver,mouseout:b.onTargetOut,mousemove:b.onMouseMove,scope:b})}}if(b.anchor){b.anchorTarget=b.target}},onMouseMove:function(d){var b=this,a,c;if(!b.target||b.target.contains(d.target)){a=b.delegate?d.getTarget(b.delegate):(b.triggerElement=true);if(a){b.targetXY=d.getXY();if(a===b.triggerElement){if(!b.hidden&&b.trackMouse){c=b.getTargetXY();if(b.constrainPosition){c=b.el.adjustForConstraints(c,b.el.parent())}b.setPagePosition(c)}}else{b.hide();b.lastActive=new Date(0);b.onTargetOver(d)}}else{if((!b.closable&&b.isVisible())&&b.autoHide!==false){b.delayHide()}}}},getTargetXY:function(){var j=this,d,c,n,a,i,l,e,m,k,b,h,g;if(j.delegate){j.anchorTarget=j.triggerElement}if(j.anchor){j.targetCounter++;c=j.getOffsets();n=(j.anchorToTarget&&!j.trackMouse)?j.getAlignToXY(j.anchorTarget,j.getAnchorAlign()):j.targetXY;a=Ext.Element.getViewportWidth()-5;i=Ext.Element.getViewportHeight()-5;l=document.documentElement;e=document.body;m=(l.scrollLeft||e.scrollLeft||0)+5;k=(l.scrollTop||e.scrollTop||0)+5;b=[n[0]+c[0],n[1]+c[1]];h=j.getSize();g=j.constrainPosition;j.anchorEl.removeCls(j.anchorCls);if(j.targetCounter<2&&g){if(b[0]a){if(j.anchorToTarget){j.defaultAlign="r-l";if(j.mouseOffset){j.mouseOffset[0]*=-1}}j.anchor="right";return j.getTargetXY()}if(b[1]i){if(j.anchorToTarget){j.defaultAlign="b-t";if(j.mouseOffset){j.mouseOffset[1]*=-1}}j.anchor="bottom";return j.getTargetXY()}}j.anchorCls="x-tip-anchor-"+j.getAnchorPosition();j.anchorEl.addCls(j.anchorCls);j.targetCounter=0;return b}else{d=j.getMouseOffset();return(j.targetXY)?[j.targetXY[0]+d[0],j.targetXY[1]+d[1]]:d}},calculateConstrainedPosition:function(b){var c=this,e,a,d;if(!b&&c.isContainedFloater()){e=c.isVisible();if(!e){c.el.show()}a=c.getTargetXY();if(!e){c.el.hide()}d=c.floatParent.getTargetEl().getViewRegion();a[0]-=d.left;a[1]-=d.top}else{a=c.callOverridden(arguments)}return a},getMouseOffset:function(){var a=this,b=a.anchor?[0,0]:[15,18];if(a.mouseOffset){b[0]+=a.mouseOffset[0];b[1]+=a.mouseOffset[1]}return b},fadeOut:function(){var a=this;a.el.fadeOut({duration:a.fadeOutDuration,callback:function(){a.hide();a.el.setOpacity("")}})},getAnchorPosition:function(){var b=this,a;if(b.anchor){b.tipAnchor=b.anchor.charAt(0)}else{a=b.defaultAlign.match(/^([a-z]+)-([a-z]+)(\?)?$/);b.tipAnchor=a[1].charAt(0)}switch(b.tipAnchor){case"t":return"top";case"b":return"bottom";case"r":return"right"}return"left"},getAnchorAlign:function(){switch(this.anchor){case"top":return"tl-bl";case"left":return"tl-tr";case"right":return"tr-tl";default:return"bl-tl"}},getOffsets:function(){var c=this,d,b,a=c.getAnchorPosition().charAt(0);if(c.anchorToTarget&&!c.trackMouse){switch(a){case"t":b=[0,9];break;case"b":b=[0,-13];break;case"r":b=[-13,0];break;default:b=[9,0];break}}else{switch(a){case"t":b=[-15-c.anchorOffset,30];break;case"b":b=[-19-c.anchorOffset,-13-c.el.dom.offsetHeight];break;case"r":b=[-15-c.el.dom.offsetWidth,-13-c.anchorOffset];break;default:b=[25,-13-c.anchorOffset];break}}d=c.getMouseOffset();b[0]+=d[0];b[1]+=d[1];return b},onTargetOver:function(d){var c=this,b=c.delegate,a;if(c.disabled||d.within(c.target.dom,true)){return}a=b?d.getTarget(b):true;if(a){c.triggerElement=a;c.triggerEvent=d;c.clearTimer("hide");c.targetXY=d.getXY();c.delayShow()}},delayShow:function(c){var a=this,b=a.el&&(c===false||!a.trackMouse)&&a.getTargetXY();if(a.hidden&&!a.showTimer){if(Ext.Date.getElapsed(a.lastActive)','',' ',"",""],initComponent:function(){var a=this;Ext.Component.prototype.initComponent.apply(this,arguments);if(a.handler){a.on("select",a.handler,a.scope,true)}},initRenderData:function(){var a=this;return Ext.apply(Ext.Component.prototype.initRenderData.call(this),{itemCls:a.itemCls,colors:a.colors})},onRender:function(){var b=this,a=b.clickEvent;Ext.Component.prototype.onRender.apply(this,arguments);b.mon(b.el,a,b.handleClick,b,{delegate:"a"});if(a!=="click"){b.mon(b.el,"click",Ext.emptyFn,b,{delegate:"a",stopEvent:true})}},afterRender:function(){var a=this,b;Ext.Component.prototype.afterRender.apply(this,arguments);if(a.value){b=a.value;a.value=null;a.select(b,true)}},handleClick:function(c){var b=this,a;c.stopEvent();if(!b.disabled){a=c.currentTarget.className.match(b.colorRe)[1];b.select(a.toUpperCase())}},select:function(b,a){var e=this,h=e.selectedCls,g=e.value,c,d;b=b.replace("#","");if(!e.rendered){e.value=b;return}if(b!==g||e.allowReselect){c=e.el;if(e.value){d=c.down("a.color-"+g,true);Ext.fly(d).removeCls(h)}d=c.down("a.color-"+b,true);Ext.fly(d).addCls(h);e.value=b;if(a!==true){e.fireEvent("select",e,b)}}},clear:function(){var b=this,c=b.value,a;if(c&&b.rendered){a=b.el.down("a.color-"+c,true);Ext.fly(a).removeCls(b.selectedCls)}b.value=null},getValue:function(){return this.value||null}},0,["colorpicker"],["component","box","colorpicker"],{component:true,box:true,colorpicker:true},["widget.colorpicker"],0,[Ext.picker,"Color",Ext,"ColorPalette"],0));(Ext.cmd.derive("Ext.layout.component.field.HtmlEditor",Ext.layout.component.field.FieldContainer,{type:"htmleditor",naturalHeight:150,naturalWidth:300,beginLayout:function(b){var a=this.owner,c;if(Ext.isGecko){c=a.textareaEl.dom;this.lastValue=c.value;c.value=""}Ext.layout.component.field.FieldContainer.prototype.beginLayout.apply(this,arguments);b.toolbarContext=b.context.getCmp(a.toolbar);b.inputCmpContext=b.context.getCmp(a.inputCmp);b.bodyCellContext=b.getEl("bodyEl");b.textAreaContext=b.getEl("textareaEl");b.iframeContext=b.getEl("iframeEl")},beginLayoutCycle:function(i){var h=this,d=i.widthModel,c=i.heightModel,b=h.owner,g=b.iframeEl,e=b.textareaEl,a=(c.natural||c.shrinkWrap)?h.naturalHeight:"";Ext.layout.component.field.FieldContainer.prototype.beginLayoutCycle.apply(this,arguments);if(d.shrinkWrap){g.setStyle("width","");e.setStyle("width","")}else{if(d.natural){i.bodyCellContext.setWidth(h.naturalWidth)}}g.setStyle("height",a);e.setStyle("height",a)},finishedLayout:function(){var a=this.owner;Ext.layout.component.field.FieldContainer.prototype.finishedLayout.apply(this,arguments);if(Ext.isGecko){a.textareaEl.dom.value=this.lastValue}}},0,0,0,0,["layout.htmleditor"],0,[Ext.layout.component.field,"HtmlEditor"],0));(Ext.cmd.derive("Ext.toolbar.Separator",Ext.toolbar.Item,{alternateClassName:"Ext.Toolbar.Separator",baseCls:"x-toolbar-separator",ariaRole:"separator"},0,["tbseparator"],["component","box","tbitem","tbseparator"],{component:true,box:true,tbitem:true,tbseparator:true},["widget.tbseparator"],0,[Ext.toolbar,"Separator",Ext.Toolbar,"Separator"],0));(Ext.cmd.derive("Ext.layout.container.boxOverflow.Menu",Ext.layout.container.boxOverflow.None,{alternateClassName:"Ext.layout.boxOverflow.Menu",noItemsMenuText:'',menuCls:"x-box-menu",constructor:function(a){var b=this;Ext.layout.container.boxOverflow.None.prototype.constructor.call(this,a);b.menuItems=[]},beginLayout:function(a){Ext.layout.container.boxOverflow.None.prototype.beginLayout.call(this,a);this.clearOverflow(a)},beginLayoutCycle:function(b,a){Ext.layout.container.boxOverflow.None.prototype.beginLayoutCycle.call(this,b,a);if(!a){this.clearOverflow(b);this.layout.cacheChildItems(b)}},onRemove:function(a){Ext.Array.remove(this.menuItems,a)},clearItem:function(a){var b=a.menu;if(a.isButton&&b){a.setMenu(b,false)}},getSuffixConfig:function(){var d=this,c=d.layout,a=c.owner,b=a.id;d.menu=new Ext.menu.Menu({listeners:{scope:d,beforeshow:d.beforeMenuShow}});d.menuTrigger=new Ext.button.Button({id:b+"-menu-trigger",cls:d.menuCls+"-after x-toolbar-item",plain:a.usePlainButtons,ownerCt:a,ownerLayout:c,iconCls:"x-"+d.getOwnerType(a)+"-more-icon",ui:a.defaultButtonUI||"default",menu:d.menu,showEmptyMenu:true,getSplitCls:function(){return""}});return d.menuTrigger.getRenderTree()},getOverflowCls:function(a){return this.menuCls+"-body-"+a},handleOverflow:function(c){var b=this,a=b.layout;b.showTrigger(c);if(a.direction!=="vertical"){b.menuTrigger.setLocalY((c.state.boxPlan.maxSize-b.menuTrigger[a.names.getHeight]())/2)}return{reservedSpace:b.triggerTotalWidth}},captureChildElements:function(){var a=this,c=a.menuTrigger,b=a.layout.names;if(c.rendering){c.finishRender();a.triggerTotalWidth=c[b.getWidth]()+c.el.getMargin(b.parallelMargins)}},clearOverflow:function(h){var g=this,b=g.menuItems,e=b.length,a=g.layout.owner,j=a._asLayoutRoot,d,c;a.suspendLayouts();g.captureChildElements();g.hideTrigger();a.resumeLayouts();for(c=0;cc){k=r.target;p.menuItems.push(k);k.hide()}}b.resumeLayouts()},hideTrigger:function(){var a=this.menuTrigger;if(a){a.hide()}},beforeMenuShow:function(j){var h=this,b=h.menuItems,d=0,a=b.length,g,e,c=function(k,i){return k.isXType("buttongroup")&&!(i instanceof Ext.toolbar.Separator)};j.suspendLayouts();j.removeAll(false);for(;d',"{[Ext.util.Format.htmlEncode(values.value)]}","","{afterTextAreaTpl}","{beforeIFrameTpl}",'',"{afterIFrameTpl}",{disableFormats:true}],stretchInputElFixed:true,subTplInsertions:["beforeTextAreaTpl","afterTextAreaTpl","beforeIFrameTpl","afterIFrameTpl","iframeAttrTpl","inputAttrTpl"],enableFormat:true,enableFontSize:true,enableColors:true,enableAlignments:true,enableLists:true,enableSourceEdit:true,enableLinks:true,enableFont:true,createLinkText:"Please enter the URL for the link:",defaultLinkValue:"http://",fontFamilies:["Arial","Courier New","Tahoma","Times New Roman","Verdana"],defaultValue:Ext.isOpera?" ":"​",extraFieldBodyCls:"x-html-editor-wrap",defaultButtonUI:"default-toolbar",initialized:false,activated:false,sourceEditMode:false,iframePad:3,hideMode:"offsets",maskOnDisable:true,containerElCls:"x-html-editor-container",reStripQuotes:/^['"]*|['"]*$/g,textAlignRE:/text-align:(.*?);/i,safariNonsenseRE:/\sclass="(?:Apple-style-span|Apple-tab-span|khtml-block-placeholder)"/gi,nonDigitsRE:/\D/g,initComponent:function(){var a=this;a.items=[a.createToolbar(),a.createInputCmp()];a.layout={type:"vbox",align:"stretch"};if(a.value==null){a.value=""}Ext.form.FieldContainer.prototype.initComponent.apply(this,arguments);a.initField()},createInputCmp:function(){this.inputCmp=Ext.widget(this.getInputCmpCfg());return this.inputCmp},getInputCmpCfg:function(){var a=this,c=a.id+"-inputCmp",b={id:c,name:a.name,textareaCls:a.textareaCls+" x-hidden",value:a.value,iframeName:Ext.id(),iframeSrc:Ext.SSL_SECURE_URL,iframeCls:"x-htmleditor-iframe"};a.getInsertionRenderData(b,a.subTplInsertions);return{flex:1,xtype:"component",tpl:a.getTpl("componentTpl"),childEls:["iframeEl","textareaEl"],id:c,cls:"x-html-editor-input",data:b}},createToolbar:function(){this.toolbar=Ext.widget(this.getToolbarCfg());return this.toolbar},getToolbarCfg:function(){var h=this,b=[],e,a=Ext.quickTipsActive&&Ext.tip.QuickTipManager.isEnabled(),d="x-",j,g;function c(l,i,k){return{itemId:l,cls:d+"btn-icon",iconCls:d+"edit-"+l,enableToggle:i!==false,scope:h,handler:k||h.relayBtnCmd,clickEvent:"mousedown",tooltip:a?h.buttonTips[l]||g:g,overflowText:h.buttonTips[l].title||g,tabIndex:-1}}if(h.enableFont&&!Ext.isSafari2){j=Ext.widget("component",{itemId:"fontSelect",renderTpl:['"],childEls:["selectEl"],afterRender:function(){h.fontSelect=this.selectEl;Ext.Component.prototype.afterRender.apply(this,arguments)},onDisable:function(){var i=this.selectEl;if(i){i.dom.disabled=true}Ext.Component.prototype.onDisable.apply(this,arguments)},onEnable:function(){var i=this.selectEl;if(i){i.dom.disabled=false}Ext.Component.prototype.onEnable.apply(this,arguments)},listeners:{change:function(){h.win.focus();h.relayCmd("fontName",h.fontSelect.dom.value);h.deferFocus()},element:"selectEl"}});b.push(j,"-")}if(h.enableFormat){b.push(c("bold"),c("italic"),c("underline"))}if(h.enableFontSize){b.push("-",c("increasefontsize",false,h.adjustFont),c("decreasefontsize",false,h.adjustFont))}if(h.enableColors){b.push("-",{itemId:"forecolor",cls:d+"btn-icon",iconCls:d+"edit-forecolor",overflowText:h.buttonTips.forecolor.title,tooltip:a?h.buttonTips.forecolor||g:g,tabIndex:-1,menu:Ext.widget("menu",{plain:true,items:[{xtype:"colorpicker",allowReselect:true,focus:Ext.emptyFn,value:"000000",plain:true,clickEvent:"mousedown",handler:function(k,i){h.relayCmd("forecolor",Ext.isWebKit||Ext.isIE?"#"+i:i);this.up("menu").hide()}}]})},{itemId:"backcolor",cls:d+"btn-icon",iconCls:d+"edit-backcolor",overflowText:h.buttonTips.backcolor.title,tooltip:a?h.buttonTips.backcolor||g:g,tabIndex:-1,menu:Ext.widget("menu",{plain:true,items:[{xtype:"colorpicker",focus:Ext.emptyFn,value:"FFFFFF",plain:true,allowReselect:true,clickEvent:"mousedown",handler:function(k,i){if(Ext.isGecko){h.execCmd("useCSS",false);h.execCmd("hilitecolor","#"+i);h.execCmd("useCSS",true);h.deferFocus()}else{h.relayCmd(Ext.isOpera?"hilitecolor":"backcolor",Ext.isWebKit||Ext.isIE||Ext.isOpera?"#"+i:i)}this.up("menu").hide()}}]})})}if(h.enableAlignments){b.push("-",c("justifyleft"),c("justifycenter"),c("justifyright"))}if(!Ext.isSafari2){if(h.enableLinks){b.push("-",c("createlink",false,h.createLink))}if(h.enableLists){b.push("-",c("insertorderedlist"),c("insertunorderedlist"))}if(h.enableSourceEdit){b.push("-",c("sourceedit",true,function(){h.toggleSourceEdit(!h.sourceEditMode)}))}}for(e=0;e",b.iframePad,a,b.defaultFont)},getEditorBody:function(){var a=this.getDoc();return a.body||a.documentElement},getDoc:function(){return this.iframeEl.dom.contentDocument||this.getWin().document},getWin:function(){return this.iframeEl.dom.contentWindow||window.frames[this.iframeEl.dom.name]},initDefaultFont:function(){var h=this,a=0,j,b,k,e,d,g,c;if(!h.defaultFont){b=h.textareaEl.getStyle("font-family");b=Ext.String.capitalize(b.split(",")[0]);j=Ext.Array.clone(h.fontFamilies);Ext.Array.include(j,b);j.sort();h.defaultFont=b;k=h.down("#fontSelect").selectEl.dom;for(d=0,g=j.length;d'+d+""}}d=g.cleanHtml(d);if(g.fireEvent("beforesync",g,d)!==false){if(Ext.isGecko&&e.value===""&&d==="
    "){d=""}if(e.value!==d){e.value=d;h=true}g.fireEvent("sync",g,d);if(h){g.checkChange()}}}},getValue:function(){var a=this,b;if(!a.sourceEditMode){a.syncValue()}b=a.rendered?a.textareaEl.dom.value:a.value;a.value=b;return b},pushValue:function(){var b=this,a;if(b.initialized){a=b.textareaEl.dom.value||"";if(!b.activated&&a.length<1){a=b.defaultValue}if(b.fireEvent("beforepush",b,a)!==false){b.getEditorBody().innerHTML=a;if(Ext.isGecko){b.setDesignMode(false);b.setDesignMode(true)}b.fireEvent("push",b,a)}}},focus:function(d,b){var c=this,e,a;if(b){if(!c.focusTask){c.focusTask=new Ext.util.DelayedTask(c.focus)}c.focusTask.delay(Ext.isNumber(b)?b:10,null,c,[d,false])}else{if(d){if(c.textareaEl&&c.textareaEl.dom){e=c.textareaEl.dom.value}if(e&&e.length){c.execCmd("selectall",true)}}a=c.getFocusEl();if(a&&a.focus){a.focus()}}return c},initEditor:function(){var d=this,c,a,h,i,b;if(d.destroying||d.destroyed){return}c=d.getEditorBody();if(!c){setTimeout(function(){d.initEditor()},10);return}a=d.textareaEl.getStyle(["font-size","font-family","background-image","background-repeat","background-color","color"]);a["background-attachment"]="fixed";c.bgProperties="fixed";Ext.DomHelper.applyStyles(c,a);h=d.getDoc();i=Ext.get(h);if(i){try{i.clearListeners()}catch(g){}b=d.onEditorEvent.bind(d);i.on({mousedown:b,dblclick:b,click:b,keyup:b,delegated:false,buffer:100});b=d.onRelayedEvent;i.on({mousedown:b,mousemove:b,mouseup:b,click:b,dblclick:b,delegated:false,scope:d});if(Ext.isGecko){i.on("keypress",d.applyCommand,d)}if(d.fixKeys){i.on("keydown",d.fixKeys,d,{delegated:false})}if(d.fixKeysAfter){i.on("keyup",d.fixKeysAfter,d,{delegated:false})}if(Ext.isIE9){Ext.get(h.documentElement).on("focus",d.focus,d)}if(Ext.isIE8){i.on("focusout",function(){d.savedSelection=h.selection.type!=="None"?h.selection.createRange():null},d);i.on("focusin",function(){if(d.savedSelection){d.savedSelection.select()}},d)}Ext.getWin().on("beforeunload",d.beforeDestroy,d);h.editorInitialized=true;d.initialized=true;d.pushValue();d.setReadOnly(d.readOnly);d.fireEvent("initialize",d)}},beforeDestroy:function(){var a=this,d=a.monitorTask,c,g;if(d){Ext.TaskManager.stop(d)}if(a.rendered){Ext.getWin().un(a.beforeDestroy,a);c=a.getDoc();if(c){Ext.get(c).destroy();if(c.hasOwnProperty){for(g in c){try{if(c.hasOwnProperty(g)){delete c[g]}}catch(b){}}}}delete a.iframeEl;delete a.textareaEl;delete a.toolbar;delete a.inputCmp}Ext.form.FieldContainer.prototype.beforeDestroy.call(this)},onRelayedEvent:function(c){var b=this.iframeEl,d=Ext.fly(b).getTrueXY(),e=c.getXY(),a=c.getXY();c.xy=[d[0]+a[0],d[1]+a[1]];c.injectEvent(b);c.xy=e},onFirstFocus:function(){var c=this,b,a;c.activated=true;c.disableItems(c.readOnly);if(Ext.isGecko){c.win.focus();b=c.win.getSelection();if(b.focusNode&&!c.getValue().length){a=b.getRangeAt(0);a.selectNodeContents(c.getEditorBody());a.collapse(true);c.deferFocus()}try{c.execCmd("useCSS",true);c.execCmd("styleWithCSS",false)}catch(d){}}c.fireEvent("activate",c)},adjustFont:function(d){var e=d.getItemId()==="increasefontsize"?1:-1,c=this.getDoc().queryCommandValue("FontSize")||"2",a=Ext.isString(c)&&c.indexOf("px")!==-1,b;c=parseInt(c,10);if(a){if(c<=10){c=1+e}else{if(c<=13){c=2+e}else{if(c<=16){c=3+e}else{if(c<=18){c=4+e}else{if(c<=24){c=5+e}else{c=6+e}}}}}c=Ext.Number.constrain(c,1,6)}else{b=Ext.isSafari;if(b){e*=2}c=Math.max(1,c+e)+(b?"px":0)}this.relayCmd("FontSize",c)},onEditorEvent:function(){this.updateToolbar()},updateToolbar:function(){var j=this,e,c,d,k,b,g,a,h;if(j.readOnly){return}if(!j.activated){j.onFirstFocus();return}d=j.getToolbar().items.map;k=j.getDoc();if(j.enableFont&&!Ext.isSafari2){g=k.queryCommandValue("fontName");b=(g?g.split(",")[0].replace(j.reStripQuotes,""):j.defaultFont).toLowerCase();a=j.fontSelect.dom;if(b!==a.value||b!==g){a.value=b}}function m(){var i;for(e=0,c=arguments.length,b;e0){g=String.fromCharCode(g);switch(g){case"b":b="bold";break;case"i":b="italic";break;case"u":b="underline";break}if(b){a.win.focus();a.execCmd(b);a.deferFocus();d.preventDefault()}}}},insertAtCursor:function(k){var i=this,h=i.getWin(),j=i.getDoc(),c,g,d,l,e,b,a;if(i.activated){h.focus();if(h.getSelection){c=h.getSelection();if(c.getRangeAt&&c.rangeCount){g=c.getRangeAt(0);g.deleteContents();d=j.createElement("div");d.innerHTML=k;l=j.createDocumentFragment();while((e=d.firstChild)){b=l.appendChild(e)}a=l.firstChild;g.insertNode(l);if(b){g=g.cloneRange();g.setStartAfter(b);g.collapse(true);c.removeAllRanges();c.addRange(g)}}}else{if(j.selection&&c.type!=="Control"){c=j.selection;g=c.createRange();g.collapse(true);c.createRange().pasteHTML(k)}}i.deferFocus()}},fixKeys:(function(){var a;if(Ext.isIE10m){return function(i){var d=this,c=i.getKey(),h=d.getDoc(),j=d.readOnly,b,g;if(c===i.TAB){i.stopEvent();if(!j){b=h.selection.createRange();if(b){if(b.collapse){b.collapse(true);b.pasteHTML("    ")}d.deferFocus()}}}}}if(Ext.isOpera){return function(d){var c=this,b=d.getKey(),g=c.readOnly;if(b===d.TAB){d.stopEvent();if(!g){c.win.focus();c.execCmd("InsertHTML","    ");c.deferFocus()}}}}return null}()),fixKeysAfter:(function(){if(Ext.isIE){return function(d){var b=this,a=d.getKey(),c=b.getDoc(),h=b.readOnly,g;if(!h&&(a===d.BACKSPACE||a===d.DELETE)){g=c.body.innerHTML;if(g==="

     

    "||g==="

     

    "){c.body.innerHTML=""}}}}return null}()),getToolbar:function(){return this.toolbar},buttonTips:{bold:{title:"Bold (Ctrl+B)",text:"Make the selected text bold.",cls:"x-html-editor-tip"},italic:{title:"Italic (Ctrl+I)",text:"Make the selected text italic.",cls:"x-html-editor-tip"},underline:{title:"Underline (Ctrl+U)",text:"Underline the selected text.",cls:"x-html-editor-tip"},increasefontsize:{title:"Grow Text",text:"Increase the font size.",cls:"x-html-editor-tip"},decreasefontsize:{title:"Shrink Text",text:"Decrease the font size.",cls:"x-html-editor-tip"},backcolor:{title:"Text Highlight Color",text:"Change the background color of the selected text.",cls:"x-html-editor-tip"},forecolor:{title:"Font Color",text:"Change the color of the selected text.",cls:"x-html-editor-tip"},justifyleft:{title:"Align Text Left",text:"Align text to the left.",cls:"x-html-editor-tip"},justifycenter:{title:"Center Text",text:"Center text in the editor.",cls:"x-html-editor-tip"},justifyright:{title:"Align Text Right",text:"Align text to the right.",cls:"x-html-editor-tip"},insertunorderedlist:{title:"Bullet List",text:"Start a bulleted list.",cls:"x-html-editor-tip"},insertorderedlist:{title:"Numbered List",text:"Start a numbered list.",cls:"x-html-editor-tip"},createlink:{title:"Hyperlink",text:"Make the selected text a hyperlink.",cls:"x-html-editor-tip"},sourceedit:{title:"Source Edit",text:"Switch to source editing mode.",cls:"x-html-editor-tip"}},privates:{deferFocus:function(){this.focus(false,true)},getFocusEl:function(){return this.sourceEditMode?this.textareaEl:this.iframeEl}}},0,["htmleditor"],["component","box","container","fieldcontainer","htmleditor"],{component:true,box:true,container:true,fieldcontainer:true,htmleditor:true},["widget.htmleditor"],[["field",Ext.form.field.Field]],[Ext.form.field,"HtmlEditor",Ext.form,"HtmlEditor"],0));(Ext.cmd.derive("Ext.form.field.Tag",Ext.form.field.ComboBox,{noWrap:false,multiSelect:true,delimiter:",",tipTpl:undefined,forceSelection:true,createNewOnEnter:false,createNewOnBlur:false,encodeSubmitValue:false,triggerOnClick:true,stacked:false,filterPickList:false,grow:true,growMin:false,growMax:false,selectOnFocus:true,fieldSubTpl:['
    ','
      ','
    • ','
      {emptyText}
      ','name="{name}" ',' value="{[Ext.util.Format.htmlEncode(values.value)]}"','size="{size}" ','tabindex="{tabIdx}" ',' disabled="disabled"','class="x-tagfield-input-field {inputElCls}" autocomplete="off">',"
    • ","
    ","
    ",{disableFormats:true}],extraFieldBodyCls:"x-tagfield-body",childEls:["listWrapper","itemList","inputEl","inputElCt","emptyEl"],emptyInputCls:"x-tagfield-emptyinput",clearValueOnEmpty:false,tagItemCls:"x-tagfield-item",tagItemTextCls:"x-tagfield-item-text",tagItemCloseCls:"x-tagfield-item-close",tagItemSelector:".x-tagfield-item",tagItemCloseSelector:".x-tagfield-item-close",tagSelectedCls:"x-tagfield-item-selected",initComponent:function(){var c=this,b=c.typeAhead,a=c.delimiter;if(c.createNewOnEnter||c.createNewOnBlur){c.forceSelection=false}c.typeAhead=false;if(c.value==null){c.value=[]}c.selectionModel=new Ext.selection.Model({mode:"MULTI",onSelectChange:function(d,g,e,h){h()},listeners:{scope:c,selectionchange:c.onSelectionChange,focuschange:c.onFocusChange}});Ext.form.field.ComboBox.prototype.initComponent.call(this);c.typeAhead=b;if(a&&c.multiSelect){c.delimiterRegexp=new RegExp(Ext.String.escapeRegex(a))}},initEvents:function(){var a=this,b=a.inputEl;Ext.form.field.ComboBox.prototype.initEvents.apply(this,arguments);if(!a.enableKeyEvents){b.on("keydown",a.onKeyDown,a);b.on("keyup",a.onKeyUp,a)}a.listWrapper.on({scope:a,click:a.onItemListClick,mousedown:a.onItemMouseDown})},isValid:function(){var b=this,a=b.disabled,c=b.forceValidation||!a;return c?b.validateValue(b.getValue()):a},onBindStore:function(a){var b=this;Ext.form.field.ComboBox.prototype.onBindStore.call(this,a);if(a){b.valueStore=new Ext.data.Store({model:a.getModel(),useModelWarning:false});b.selectionModel.bindStore(b.valueStore);if(b.filterPickList){b.listFilter=new Ext.util.Filter({scope:b,filterFn:b.filterPicked});b.changingFilters=true;a.filter(b.listFilter);b.changingFilters=false}}},filterPicked:function(a){return !this.valueCollection.contains(a)},onUnbindStore:function(a){var c=this,d=c.valueStore,b=c.picker;if(b){b.bindStore(null)}if(d){d.destroy();c.valueStore=null}if(c.filterPickList&&!a.destroyed){c.changingFilters=true;a.removeFilter(c.listFilter);c.changingFilters=false}Ext.form.field.ComboBox.prototype.onUnbindStore.apply(this,arguments)},onValueCollectionEndUpdate:function(){var a=this,c=a.valueCollection.items,b=a.valueStore;if(a.isSelectionUpdating()){return}if(a.filterPickList){a.changingFilters=true;a.store.filter(a.listFilter);a.changingFilters=false}Ext.form.field.ComboBox.prototype.onValueCollectionEndUpdate.call(this);Ext.suspendLayouts();if(b){b.suspendEvents();b.loadRecords(c);b.resumeEvents()}Ext.resumeLayouts(true);a.alignPicker()},checkValueOnDataChange:Ext.emptyFn,onSelectionChange:function(a,b){this.applyMultiselectItemMarkup();this.fireEvent("valueselectionchange",this,b)},onFocusChange:function(a,c,b){this.fireEvent("valuefocuschange",this,c,b)},onDestroy:function(){this.selectionModel=Ext.destroy(this.selectionModel);Ext.form.field.ComboBox.prototype.onDestroy.apply(this,arguments)},getSubTplData:function(c){var i=this,h=Ext.form.field.ComboBox.prototype.getSubTplData.apply(this,arguments),d=i.emptyText,e=i.emptyInputCls,g=d&&h.value.length<1,j=i.growMin,a=i.growMax,b="";h.value="";h.emptyText=g?d:"";h.emptyCls=g?i.emptyCls:e;h.inputElCls=g?e:"";h.itemListCls="";if(i.grow){if(Ext.isNumber(j)&&j>0){b+="min-height:"+j+"px;"}if(Ext.isNumber(a)&&a>0){b+="max-height:"+a+"px;"}}h.wrapperStyle=b;if(i.stacked===true){h.itemListCls+=" x-tagfield-stacked"}if(!i.multiSelect){h.itemListCls+=" x-tagfield-singleselect"}return h},afterRender:function(){var b=this,c=b.inputEl,a=b.emptyText;if(a){if(Ext.supports.Placeholder&&c){c.dom.removeAttribute("placeholder")}else{b.applyEmptyText()}}b.applyMultiselectItemMarkup();Ext.form.field.ComboBox.prototype.afterRender.apply(this,arguments)},findRecord:function(c,b){var a=this.getStore().queryRecords(c,b);return a.length?a[0]:false},getCursorPosition:function(){var a;if(document.selection){a=document.selection.createRange();a.collapse(true);a.moveStart("character",-this.inputEl.dom.value.length);a=a.text.length}else{a=this.inputEl.dom.selectionStart}return a},hasSelectedText:function(){var c=this.inputEl.dom,b,a;if(document.selection){b=document.selection;a=b.createRange();return(a.parentElement()===c)}else{return c.selectionStart!==c.selectionEnd}},onKeyDown:function(g){var h=this,k=g.getKey(),j=h.inputEl,c=j.dom.value,a=h.valueCollection,d=h.selectionModel,b=false,i;if(h.readOnly||h.disabled||!h.editable){return}if(a.getCount()>0&&(c===""||(h.getCursorPosition()===0&&!h.hasSelectedText()))){i=(d.getCount()>0)?a.indexOf(d.getLastSelected()):-1;if(k===g.BACKSPACE||k===g.DELETE){if(i>-1){if(d.getCount()>1){i=-1}a.remove(d.getSelection())}else{a.remove(a.last())}d.clearSelections();if(i>0){d.select(i-1)}else{if(a.getCount()){d.select(a.last())}}b=true}else{if(k===g.RIGHT||k===g.LEFT){if(i===-1&&k===g.LEFT){d.select(a.last());b=true}else{if(i>-1){if(k===g.RIGHT){if(i<(a.getCount()-1)){d.select(i+1,g.shiftKey);b=true}else{if(!g.shiftKey){d.deselectAll();b=true}}}else{if(k===g.LEFT&&(i>0)){d.select(i-1,g.shiftKey);b=true}}}}}else{if(k===g.A&&g.ctrlKey){d.selectAll();b=g.A}}}}if(b){h.preventKeyUpEvent=b;g.stopEvent();return}if(h.isExpanded&&k===g.ENTER&&h.picker.highlightedItem){h.preventKeyUpEvent=true}if(h.enableKeyEvents){Ext.form.field.ComboBox.prototype.onKeyDown.apply(this,arguments)}if(!g.isSpecialKey()&&!g.hasModifier()){d.deselectAll()}},onKeyUp:function(h,b){var c=this,g=c.inputEl,d=g.dom.value,a=c.preventKeyUpEvent;if(c.preventKeyUpEvent){h.stopEvent();if(a===true||h.getKey()===a){delete c.preventKeyUpEvent}return}if(c.multiSelect&&c.delimiterRegexp&&c.delimiterRegexp.test(d)||(c.createNewOnEnter&&h.getKey()===h.ENTER)){d=Ext.Array.clean(d.split(c.delimiterRegexp));g.dom.value="";c.setValue(c.valueStore.getRange().concat(d));g.focus()}Ext.form.field.ComboBox.prototype.onKeyUp.call(this,h,b)},onTypeAhead:function(){var g=this,e=g.displayField,d=g.inputEl.dom,c=g.getPicker(),b=g.getStore().findRecord(e,d.value),h,a,i;if(b){h=b.get(e);a=h.length;i=d.value.length;c.highlightItem(c.getNode(b));if(i!==0&&i!==a){d.value=h;g.selectText(i,h.length)}}},onItemListClick:function(d){var c=this,a=c.selectionModel,b=d.getTarget(c.tagItemSelector),g=b?d.getTarget(c.tagItemCloseSelector):false;if(c.readOnly||c.disabled){return}d.stopPropagation();if(b){if(g){c.removeByListItemNode(b);if(c.valueStore.getCount()>0){c.fireEvent("select",c,c.valueStore.getRange())}}else{c.toggleSelectionByListItemNode(b,d.shiftKey)}if(!Ext.supports.TouchEvents){c.inputEl.focus()}}else{if(a.getCount()>0){a.deselectAll()}c.inputEl.focus();if(c.triggerOnClick){c.onTriggerClick()}}},onItemMouseDown:function(a){a.preventDefault()},getMultiSelectItemMarkup:function(){var b=this,a=(b._getChildElCls&&b._getChildElCls())||"";if(!b.multiSelectItemTpl){if(!b.labelTpl){b.labelTpl="{"+b.displayField+"}"}b.labelTpl=b.getTpl("labelTpl");if(b.tipTpl){b.tipTpl=b.getTpl("tipTpl")}b.multiSelectItemTpl=new Ext.XTemplate(['','
  • '," "+b.tagSelectedCls,"","{%","values = values.data;","%}",b.tipTpl?'" data-qtip="{[this.getTip(values)]}">':'">','
    {[this.getItemLabel(values)]}
    ','
    ',"
  • ","
    ",{isSelected:function(c){return b.selectionModel.isSelected(c)},getItemLabel:function(c){return Ext.String.htmlEncode(b.labelTpl.apply(c))},getTip:function(c){return Ext.String.htmlEncode(b.tipTpl.apply(c))},strict:true}])}if(!b.multiSelectItemTpl.isTemplate){b.multiSelectItemTpl=this.getTpl("multiSelectItemTpl")}return b.multiSelectItemTpl.apply(b.valueCollection.getRange())},applyMultiselectItemMarkup:function(){var b=this,a=b.itemList;if(a){a.select(".x-tagfield-item").destroy();b.inputElCt.insertHtml("beforeBegin",b.getMultiSelectItemMarkup());b.autoSize()}},getRecordByListItemNode:function(a){return this.valueCollection.items[Number(a.getAttribute("data-selectionIndex"))]},toggleSelectionByListItemNode:function(b,d){var c=this,e=c.getRecordByListItemNode(b),a=c.selectionModel;if(e){if(a.isSelected(e)){a.deselect(e)}else{a.select(e,d)}}},removeByListItemNode:function(a){var b=this,c=b.getRecordByListItemNode(a);if(c){b.pickerSelectionModel.deselect(c)}},getDisplayValue:function(){return this.getRawValue()},getRawValue:function(){var e=this,c=e.getValueRecords(),b=[],d,a;for(d=0,a=c.length;d0||j.isLoaded(),b=j.hasPendingLoad(),m=p&&!g&&!b,e,r,q,o,c,s;if(Ext.isEmpty(n)){n=null}else{if(Ext.isString(n)&&t.multiSelect){n=n.split(t.delimiter)}else{n=Ext.Array.from(n,true)}}if(n&&t.queryMode==="remote"&&!j.isEmptyStore&&d!==true&&m){for(q=0,r=n.length;q-1){n[q]=h.getAt(o)}else{o=t.findRecord(a,e);if(!o){if(t.forceSelection){k.push(e)}else{o={};o[t.valueField]=e;o[t.displayField]=e;c=t.valueStore.getModel();o=new c(o)}}if(o){n[q]=o}}}}if(k.length){s={};s[t.valueParam||t.valueField]=k.join(t.delimiter);j.load({params:s,callback:function(){t.setValue(n,l,true);t.autoSize();t.lastQuery=false}});return false}}if(!t.multiSelect&&n.length>0){for(q=n.length-1;q>=0;q--){if(n[q].isModel){n=n[q];break}}if(Ext.isArray(n)){n=n[n.length-1]}}return Ext.form.field.ComboBox.prototype.setValue.call(this,n,l)},updateValue:function(){var d=this,c=d.valueCollection.getRange(),a=c.length,b;for(b=0;b=b&&h<=a}});d.add(c);d.endUpdate()}},0,["timepicker"],["component","box","dataview","boundlist","timepicker"],{component:true,box:true,dataview:true,boundlist:true,timepicker:true},["widget.timepicker"],0,[Ext.picker,"Time"],function(){this.prototype.modelType=Ext.define(null,{extend:"Ext.data.Model",fields:["disp","date"]})}));(Ext.cmd.derive("Ext.form.field.Time",Ext.form.field.ComboBox,{alternateClassName:["Ext.form.TimeField","Ext.form.Time"],triggerCls:"x-form-time-trigger",minText:"The time in this field must be equal to or after {0}",maxText:"The time in this field must be equal to or before {0}",invalidText:"{0} is not a valid time",format:"g:i A",altFormats:"g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H|gi a|hi a|giA|hiA|gi A|hi A",formatText:"Expected time format: HH:MM space am/pm",increment:15,pickerMaxHeight:300,selectOnTab:true,snapToIncrement:false,valuePublishEvent:["select","blur"],initDate:"1/1/2008",initDateParts:[2008,0,1],initDateFormat:"j/n/Y",queryMode:"local",displayField:"disp",valueField:"date",initComponent:function(){var c=this,b=c.minValue,a=c.maxValue;if(b){c.setMinValue(b)}if(a){c.setMaxValue(a)}c.displayTpl=new Ext.XTemplate('{[typeof values === "string" ? values : this.formatDate(values["'+c.displayField+'"])]}'+c.delimiter+"",{formatDate:c.formatDate.bind(c)});c.store=Ext.picker.Time.createStore(c.format,c.increment);Ext.form.field.ComboBox.prototype.initComponent.call(this);c.getPicker()},isEqual:function(g,e){var b=Ext.Array.from,d=Ext.Date.isEqual,c,a;g=b(g);e=b(e);a=g.length;if(a!==e.length){return false}for(c=0;c0?l:this.getRawValue();var g=this,k=Ext.String.format,j=Ext.form.field.ComboBox.prototype.getErrors.call(this,l),h=g.minValue,e=g.maxValue,c=g.displayTplData,m=g.getRawValue(),b,d,a,n;if(c&&c.length>0){for(b=0,d=c.length;be){j.push(k(g.maxText,g.formatDate(e)))}}}else{if(m.length&&!g.parseDate(m)){j.push(k(g.invalidText,m,Ext.Date.unescapeFormat(g.format)))}}return j},formatDate:function(b){var d=[],c,a;b=Ext.Array.from(b);for(c=0,a=b.length;c=a.length){b.colIdx=0}else{b.colIdx++}b.setColumn(b.colIdx)}while(!b.getCell(true));break}}},statics:{compare:function(b,a){return b.rowIdx-a.rowIdx||b.colIdx-a.colIdx}}},1,0,0,0,0,0,[Ext.grid,"CellContext"],0));(Ext.cmd.derive("Ext.grid.CellEditor",Ext.Editor,{isCellEditor:true,alignment:"l-l!",hideEl:false,cls:"x-small-editor x-grid-editor x-grid-cell-editor",treeNodeSelector:".x-tree-node-text",shim:false,shadow:false,setGrid:function(b){var d=this,c=d.grid,a;if(b!==c){a={beforeitemupdate:d.beforeItemUpdate,itemupdate:d.onItemUpdate,scope:d};if(c){c.getView().un(a)}d.grid=b;b.getView().on(a)}},beforeViewRefresh:function(a){var b=this,c=b.el&&b.el.dom;if(c){b.wasAllowBlur=b.allowBlur;if(b.editing){a.cellFocused=false;b.allowBlur=false}if(c.parentNode){a.refreshing=true;c.parentNode.removeChild(c)}}},onViewRefresh:function(b){var d=this,e=d.el&&d.el.dom,a,c=d.context;if(e){a=b.getCellByPosition(c,true);if(!a){d.allowBlur=d.wasAllowBlur;d.completeEdit();Ext.getDetachedBody().dom.appendChild(e);return}c.node=b.getNode(c.record);c.row=b.getRow(c.record);c.cell=a;c.rowIdx=b.indexOf(c.row);a.insertBefore(e,a.firstChild);d.boundEl=d.container=Ext.get(a);d.realign(true);if(d.editing){if(Ext.isIE){Ext.defer(function(){if(!d.destroyed){d.allowBlur=d.wasAllowBlur;d.field.focus()}},10)}else{d.allowBlur=d.wasAllowBlur;d.field.focus()}}}},beforeItemUpdate:function(c,d,e,b){var j=this,h=j.context,a=b.length,g;if(c===h.record){for(g=0;g:not([hidden])").length===0){b=a.getTriggerElWidth()}}return b}},0,0,0,0,["layout.columncomponent"],0,[Ext.grid,"ColumnComponentLayout"],0));(Ext.cmd.derive("Ext.layout.container.Fit",Ext.layout.container.Container,{alternateClassName:"Ext.layout.FitLayout",itemCls:"x-fit-item",type:"fit",manageMargins:true,sizePolicies:{0:{readsWidth:1,readsHeight:1,setsWidth:0,setsHeight:0},1:{readsWidth:0,readsHeight:1,setsWidth:1,setsHeight:0},2:{readsWidth:1,readsHeight:0,setsWidth:0,setsHeight:1},3:{readsWidth:0,readsHeight:0,setsWidth:1,setsHeight:1}},getItemSizePolicy:function(b,c){var a=c||this.owner.getSizeModel(),d=(a.width.shrinkWrap?0:1)|(a.height.shrinkWrap?0:2);return this.sizePolicies[d]},beginLayoutCycle:function(k,g){var t=this,u=t.lastHeightModel&&t.lastHeightModel.calculated,h=t.lastWidthModel&&t.lastWidthModel.calculated,o=h||u,l=0,m=0,s,b,p,r,e,a,j,n,q,d;Ext.layout.container.Container.prototype.beginLayoutCycle.apply(this,arguments);if(o&&k.targetContext.el.dom.tagName.toUpperCase()!=="TD"){o=h=u=false}b=k.childItems;e=b.length;for(p=0;p0){for(j=0;j'+c.view.emptyText+""}c.view.getComponentLayout().headerCt=c.headerCt;c.mon(c.view,{uievent:c.processEvent,scope:c});c.headerCt.view=c.view;if(c.hasListeners.viewcreated){c.fireEvent("viewcreated",c,c.view)}}return c.view},getColumnManager:function(){return this.columnManager},getVisibleColumnManager:function(){return this.visibleColumnManager},getTopLevelColumnManager:function(){return this.ownerGrid.getColumnManager()},getTopLevelVisibleColumnManager:function(){return this.ownerGrid.getVisibleColumnManager()},setAutoScroll:Ext.emptyFn,applyScrollable:function(a){if(this.view){this.view.setScrollable(a)}return a},getScrollable:function(){return null},processEvent:function(g,i,j,a,h,d,c,k){var b=d.position.column;if(b){return b.processEvent.apply(b,arguments)}},ensureVisible:function(a,b){this.doEnsureVisible(a,b)},scrollByDeltaY:function(b,a){this.getView().scrollBy(0,b,a)},scrollByDeltaX:function(b,a){this.getView().scrollBy(b,0,a)},afterCollapse:function(){this.saveScrollPos();Ext.panel.Panel.prototype.afterCollapse.apply(this,arguments)},afterExpand:function(){Ext.panel.Panel.prototype.afterExpand.apply(this,arguments);this.restoreScrollPos()},saveScrollPos:Ext.emptyFn,restoreScrollPos:Ext.emptyFn,onHeaderResize:function(){var a=this.view.getScrollable(),b;if(a&&a.isTouchScroller){b=a.getSize();if(b){a.setSize({x:this.headerCt.getTableWidth(),y:b.y})}}},onHeaderMove:function(e,g,a,b,d){var c=this;if(c.optimizedColumnMove===false){c.view.refreshView()}else{c.view.moveColumn(b,d,a)}c.delayScroll()},onHeaderHide:function(b,c){var a=this.view;if(!b.childHideCount&&a.refreshCounter){a.refreshView()}},onHeaderShow:function(b,c){var a=this.view;if(a.refreshCounter){a.refreshView()}},onHeadersChanged:function(b,c){var a=this;if(a.rendered&&!a.reconfiguring){a.view.refreshView();a.delayScroll()}},delayScroll:function(){var a=this.view;if(a){this.scrollTask.delay(10,null,null,[a])}},onViewReady:function(){this.fireEvent("viewready",this)},onRestoreHorzScroll:function(){var b=this,a=b.scrollXPos;if(a){b.syncHorizontalScroll(b,true)}},getScrollerOwner:function(){var a=this;if(!this.scrollerOwner){a=this.up("[scrollerOwner]")}return a},getLhsMarker:function(){var a=this;return a.lhsMarker||(a.lhsMarker=Ext.DomHelper.append(a.el,{role:"presentation",cls:a.resizeMarkerCls},true))},getRhsMarker:function(){var a=this;return a.rhsMarker||(a.rhsMarker=Ext.DomHelper.append(a.el,{role:"presentation",cls:a.resizeMarkerCls},true))},getSelection:function(){return this.getSelectionModel().getSelection()},updateSelection:function(a){var b=this,c;if(!b.ignoreNextSelection){b.ignoreNextSelection=true;c=b.getSelectionModel();if(a){c.select(a)}else{c.deselectAll()}b.ignoreNextSelection=false}},updateBindSelection:function(a,c){var d=this,b=null;if(!d.ignoreNextSelection){d.ignoreNextSelection=true;if(c.length){b=a.getLastSelected();d.hasHadSelection=true}if(d.hasHadSelection){d.setSelection(b)}d.ignoreNextSelection=false}},updateHeaderBorders:function(a){this[a?"removeCls":"addCls"](this.noHeaderBordersCls)},getNavigationModel:function(){return this.getView().getNavigationModel()},getSelectionModel:function(){return this.getView().getSelectionModel()},getScrollTarget:function(){var a=this.getScrollerOwner().query("tableview");return a[a.length-1]},syncHorizontalScroll:function(e,c){var d=this,a=d.view.getScrollX(),b;c=c===true;if(d.rendered&&(c||a!==d.scrollXPos)){if(c){b=d.getScrollTarget();b.setScrollX(a)}d.headerCt.setScrollX(a);d.scrollXPos=a}},onStoreLoad:Ext.emptyFn,getEditorParent:function(){return this.body},bindStore:function(b,c){var d=this,a=d.getView();if(b){d.store=b;if(a.store!==b){a.bindStore(b,false)}d.mon(b,{load:d.onStoreLoad,scope:d});d.storeRelayers=d.relayEvents(b,["filterchange","groupchange"])}else{d.unbindStore()}},unbindStore:function(){var c=this,b=c.store,a;if(b){b.trackStateChanges=false;c.store=null;c.mun(b,{load:c.onStoreLoad,scope:c});Ext.destroy(c.storeRelayers);a=c.view;if(a.store){a.bindStore(null)}}},setColumns:function(a){if(a.length||this.getColumnManager().getColumns().length){this.reconfigure(undefined,a)}},setStore:function(a){this.reconfigure(a);if(this.autoLoad&&!a.isEmptyStore&&!(a.loading||a.isLoaded())){a.load()}},reconfigure:function(k,c){var h=this,a=h.store,b=h.headerCt,j=h.lockable,e=b?b.items.getRange():h.columns,i=h.getView(),d,g;if(arguments.length===1&&Ext.isArray(k)){c=k;k=null}if(c){c=Ext.Array.slice(c)}h.reconfiguring=true;if(k){k=Ext.StoreManager.lookup(k)}h.fireEvent("beforereconfigure",h,k,c,a,e);Ext.suspendLayouts();if(j){h.reconfigureLockable(k,c)}else{d=i.blockRefresh;i.blockRefresh=true;if(k&&k!==a){h.unbindStore();h.bindStore(k)}if(c){delete h.scrollXPos;b.removeAll();b.add(c)}i.blockRefresh=d;g=i.refreshCounter}Ext.resumeLayouts(true);if(j){h.afterReconfigureLockable()}else{if(i.refreshCounter===g){i.refreshView()}}h.fireEvent("reconfigure",h,k,c,a,e);delete h.reconfiguring},beforeDestroy:function(){var b=this,a=b.scrollTask;if(a){a.cancel();b.scrollTask=null}Ext.destroy(b.focusEnterLeaveListeners);Ext.panel.Panel.prototype.beforeDestroy.call(this)},onDestroy:function(){var a=this;if(a.lockable){a.destroyLockable()}a.unbindStore();Ext.panel.Panel.prototype.onDestroy.call(this);a.columns=a.storeRelayers=a.columnManager=a.visibleColumnManager=null},destroy:function(){var a=this;Ext.panel.Panel.prototype.destroy.call(this);if(a.destroyed){a.view=a.selModel=a.headerCt=null}},privates:{initFocusableElement:function(){},doEnsureVisible:function(e,o){if(this.lockable){return this.ensureLockedVisible(e,o)}if(typeof e!=="number"&&!e.isEntity){e=this.store.getById(e)}var g=this,i=g.getView(),d=i.getNode(e),l,m,a,b,j,n,h,c,k;if(o){l=o.callback;m=o.scope;a=o.animate;b=o.highlight;j=o.select;n=o.focus;c=o.column}if(g.deferredEnsureVisible){g.deferredEnsureVisible.destroy()}if(!i.componentLayoutCounter){g.deferredEnsureVisible=i.on({resize:g.doEnsureVisible,args:Ext.Array.slice(arguments),scope:g,single:true,destroyable:true});return}if(typeof c==="number"){c=g.ownerGrid.getVisibleColumnManager().getColumns()[c]}if(d){h=i.getScrollable();if(c){k=Ext.fly(d).selectNode(c.getCellSelector())}if(h){h.scrollIntoView(k||d,!!c,a,b)}if(!e.isEntity){e=i.getRecord(d)}if(j){i.getSelectionModel().select(e)}if(n){i.getNavigationModel().setPosition(e,0)}Ext.callback(l,m||g,[true,e,d])}else{if(i.bufferedRenderer){i.bufferedRenderer.scrollTo(e,{animate:a,highlight:b,select:j,focus:n,column:c,callback:function(r,p,q){Ext.callback(l,m||g,[true,p,q])}})}else{Ext.callback(l,m||g,[false,null])}}},getFocusEl:function(){return this.getView().getFocusEl()},setActionableMode:function(b,a){var c=this.ownerGrid;if(!c.destroying&&c.view.setActionableMode(b,a)!==false){c.fireEvent("actionablemodechange",b);c[b?"addCls":"removeCls"](c.actionableModeCls);return true}}}},1,["tablepanel"],["component","box","container","panel","tablepanel"],{component:true,box:true,container:true,panel:true,tablepanel:true},["widget.tablepanel"],0,[Ext.panel,"Table"],0));(Ext.cmd.derive("Ext.grid.ColumnLayout",Ext.layout.container.HBox,{type:"gridcolumn",firstHeaderCls:"x-column-header-first",lastHeaderCls:"x-column-header-last",initLayout:function(){Ext.layout.container.HBox.prototype.initLayout.call(this);if(this.scrollbarWidth===undefined){this.self.prototype.scrollbarWidth=Ext.getScrollbarSize().width}},beginLayout:function(c){var j=this,a=j.owner,d=j.firstHeaderCls,l=j.lastHeaderCls,b=[d,l],h=j.getVisibleItems(),g=h.length,e,k;Ext.layout.container.HBox.prototype.beginLayout.call(this,c);for(e=0;e0){a=this.getColumns()[b-1]}return a},getNextSibling:function(c){var b=this.getHeaderIndex(c),a;if(b!==-1){a=this.getColumns()[b+1]}return a||null},getFirst:function(){var a=this.getColumns();return a.length>0?a[0]:null},getLast:function(){var b=this.getColumns(),a=b.length;return a>0?b[a-1]:null},getHeaderByDataIndex:function(d){var c=this.getColumns(),a=c.length,b,e;for(b=0;b=k.left&&l=k.getMaxPosition().y-q.all.last(true).offsetHeight){g.rowIdx--}b=Math.min(g.rowIdx,a.getCount()-1);d=Math.min(d,c.getColumns().length);l=a.getAt(b);h=c.getColumns()[d]}}else{if(p){l=b=null}else{if(m==null){m=o.lastFocused?o.lastFocused.column:0}if(typeof g==="number"){b=Math.max(Math.min(g,a.getCount()-1),0);l=a.getAt(g)}else{if(g.isEntity){l=g;b=a.indexOf(l)}else{if(g.tagName){l=q.getRecord(g);b=a.indexOf(l);if(b===-1){l=null}}else{if(e){return}p=true;l=b=null}}}}if(l){if(b===-1){o.recordIndex=-1;l=a.getAt(0);b=0;m=null}if(m==null){if(!(h=o.column)){d=0;h=c.getColumns()[0]}}else{if(typeof m==="number"){h=c.getColumns()[m];d=m}else{h=m;d=c.indexOf(m)}}}else{p=true;h=d=null}}if(b===o.recordIndex&&d===o.columnIndex&&q===o.position.view){return o.focusPosition(o.position)}if(o.cell){o.cell.removeCls(o.focusCls)}o.previousRecordIndex=o.recordIndex;o.previousRecord=o.record;o.previousItem=o.item;o.previousCell=o.cell;o.previousColumn=o.column;o.previousColumnIndex=o.columnIndex;o.previousPosition=o.position.clone();o.selectionStart=j.selectionStart;o.position.setAll(q,o.recordIndex=b,o.columnIndex=d,o.record=l,o.column=h);if(p){o.item=o.cell=null}else{o.focusPosition(o.position,i)}if(!r){j.fireEvent("focuschange",j,o.previousRecord,o.record);q.fireEvent("rowfocus",o.record,o.item,o.recordIndex);q.fireEvent("cellfocus",o.record,o.cell,o.position)}if(n&&!i&&o.cell!==o.previousCell){o.fireNavigateEvent(n)}},focusPosition:function(a){var c=this,b,d;c.item=c.cell=null;if(a&&a.record&&a.column){b=a.view;if(a.rowElement){d=c.item=a.rowElement}else{d=b.getRowByRecord(a.record)}if(d){c.cell=a.cellElement||Ext.fly(d).down(a.column.getCellSelector(),true);if(c.cell){c.cell=new Ext.dom.Fly(c.cell);b.lastFocused=c.lastFocused=c.position.clone();c.focusItem(c.cell);b.focusEl=c.cell}else{c.position.setAll();c.record=c.column=c.recordIndex=c.columnIndex=null}}else{d=b.dataSource.indexOf(a.record);c.position.setAll();c.record=c.column=c.recordIndex=c.columnIndex=null;if(d!==-1&&b.bufferedRenderer){c.lastKeyEvent=null;b.bufferedRenderer.scrollTo(d,false,c.afterBufferedScrollTo,c)}}}},focusItem:function(a){a.addCls(this.focusCls);a.focus()},getCell:function(){return this.cell},getPosition:function(c){var e=this,a=e.position,d,b,g;if(a.record&&a.column){if(c){return a}b=a.view;g=b.dataSource;d=g.indexOf(a.record);if(d===-1){d=a.rowIdx;if(!(a.record=g.getAt(d))){d=-1}}if(d===-1||b.getVisibleColumnManager().indexOf(a.column)===-1){a.setAll();e.record=e.column=e.recordIndex=e.columnIndex=null}else{return a}}return null},getLastFocused:function(){var c=this,a,b=c.lastFocused;if(b&&b.record&&b.column){a=b.view;if(a.dataSource.indexOf(b.record)!==-1&&a.getVisibleColumnManager().indexOf(b.column)!==-1){return b}}},onKeyTab:function(e){var b=!e.shiftKey,c=e.position.clone(),g=c.view,l=e.position.cellElement,h=Ext.fly(l).findTabbableElements(),j,k=g.ownerGrid.actionables,d=k.length,a;e.preventDefault();j=h[Ext.Array.indexOf(h,e.target)+(b?1:-1)];while(!j&&(l=l[b?"nextSibling":"previousSibling"])){c.setColumn(g.getHeaderByCell(l));for(a=0;am.height}e.setProp("viewOverflowY",i)}},measureContentHeight:function(d){var a=this.owner,c=a.body.dom,b=a.emptyEl,e=0;if(b){e+=b.offsetHeight}if(c){e+=c.offsetHeight}if(d.headerContext.state.boxPlan.tooNarrow){e+=Ext.getScrollbarSize().height}return e},flushColumnWidths:function(){var l=this,k=l.layout,b=l.ownerContext,d=l.columnsChanged,a=b.target,j=d.length,c,g,e,h;if(b.state.columnFlusher!==l){return}for(g=0;g0){i+=k;Ext.fly(c[a].el).setHeight(d)}else{b-=k}}d=g.rowHeight+b;if(Ext.isIE9&&h.view.ownerGrid.rowLines){d--}if(h.rowHeight+i=g+a;c--){e[c]=e[c-a];e[c].setAttribute("data-recordIndex",c)}}d.endIndex=d.endIndex+a}else{d.startIndex=g;d.endIndex=g+a-1}for(c=0;cg.endIndex){delete h[c]}}while(c!==b);delete h[c]},getCount:function(){return this.count},slice:function(e,b){var d=this.elements,a=[],c;if(!b){b=this.endIndex}else{b=Math.min(this.endIndex,b-1)}for(c=e||this.startIndex;c<=b;c++){a.push(d[c])}return a},replaceElement:function(d,c,a){var e=this.elements,b=(typeof d==="number")?d:this.indexOf(d);if(b>-1){c=Ext.getDom(c);if(a){d=e[b];d.parentNode.insertBefore(c,d);Ext.removeNode(d);c.setAttribute("data-recordIndex",b)}this.elements[b]=c}return this},indexOf:function(b){var c=this.elements,a;b=Ext.getDom(b);for(a=this.startIndex;a<=this.endIndex;a++){if(c[a]===b){return a}}return -1},removeRange:function(c,g,d){var k=this,a=k.elements,j=[],e,h,b,l;if(g==null){g=k.endIndex+1}else{g=Math.min(k.endIndex+1,g+1)}if(c==null){c=k.startIndex}b=g-c;for(h=c,l=g;h<=k.endIndex;h++,l++){e=a[h];if(h=h.startIndex&&j<=h.endIndex){l[l.length]=j}}Ext.Array.sort(l);e=l.length}else{if(lh.endIndex){return}e=1;l=[l]}for(g=i=l[0],b=0;g<=h.endIndex;g++,i++){if(b=h.startIndex){d=a[g]=a[i];d.setAttribute("data-recordIndex",g)}else{delete a[g]}}h.endIndex-=e;h.count-=e},scroll:function(s,t,j){var u=this,l=u.view,g=l.store,m=u.elements,a=s.length,p=l.getNodeContainer(),h=l.hasListeners.itemremove,r=l.hasListeners.itemadd,n=u.statics().range,q,b,c,d,k,e,o,v;if(!s.length){return}if(t===-1){if(j){if(h){o=[];v=[]}e=(u.endIndex-j)+1;if(n){n.setStartBefore(m[e]);n.setEndAfter(m[u.endIndex]);n.deleteContents();for(q=e;q<=u.endIndex;q++){b=m[q];delete m[q];if(h){o.push(g.getByInternalId(b.getAttribute("data-recordId")));v.push(b)}}}else{for(q=e;q<=u.endIndex;q++){b=m[q];delete m[q];Ext.removeNode(b);if(h){o.push(g.getByInternalId(b.getAttribute("data-recordId")));v.push(b)}}}l.fireEvent("itemremove",o,e,v,l);u.endIndex-=j}if(s.length){k=l.bufferRender(s,u.startIndex-=a);d=k.children;for(q=0;q',"{[view.renderTHead(values, out, parent)]}","{%","view.renderRows(values.rows, values.columns, values.viewStartIndex, out);","%}","{[view.renderTFoot(values, out, parent)]}","","{% ","view = columns = column = null;","%}",{definitions:"var view, tableCls, columns, i, len, column;",priority:0}],outerRowTpl:['',"{%","this.nextTpl.applyOut(values, out, parent)","%}","",{priority:9999}],rowTpl:["{%",'var dataRowCls = values.recordIndex === -1 ? "" : " x-grid-row";',"%}",'','{%',"parent.view.renderCell(values, parent.record, parent.recordIndex, parent.rowIndex, xindex - 1, out, parent)","%}","","",{priority:0}],cellTpl:['{tdStyle}"',' tabindex="-1" data-columnid="{[values.column.getItemId()]}">','
    {style}" ',"{cellInnerAttr:attributes}>{value}
    ","",{priority:0}],refreshSelmodelOnRefresh:false,tableValues:{},rowValues:{itemClasses:[],rowClasses:[]},cellValues:{classes:["x-grid-cell x-grid-td"]},constructor:function(a){if(a.grid.isTree){a.baseCls="x-tree-view"}Ext.view.View.prototype.constructor.call(this,a)},hasVariableRowHeight:function(a){var b=this;return b.variableRowHeight||b.store.isGrouped()||b.getVisibleColumnManager().hasVariableRowHeight()||(!a&&b.lockingPartner&&b.lockingPartner.hasVariableRowHeight(true))},initComponent:function(){var a=this;if(a.columnLines){a.addCls(a.grid.colLinesCls)}if(a.rowLines){a.addCls(a.grid.rowLinesCls)}a.body=new Ext.dom.Fly();a.body.id=a.id+"gridBody";if(!a.trackOver){a.overItemCls=null}a.headerCt.view=a;a.grid.view=a;a.initFeatures(a.grid);a.itemSelector=a.getItemSelector();a.all=new Ext.view.NodeCache(a);Ext.view.View.prototype.initComponent.call(this)},applySelectionModel:function(b,e){var d=this,c=d.ownerGrid,g=b.type,a=d.disableSelection||c.disableSelection;if(!e){if(!(b&&b.isSelectionModel)){b=c.selModel||b}}if(b){if(b.isSelectionModel){b.allowDeselect=c.allowDeselect||b.selectionMode!=="SINGLE";b.locked=a}else{if(typeof b==="string"){b={type:b}}else{b.type=c.selType||b.selType||b.type||g}if(!b.mode){if(c.simpleSelect){b.mode="SIMPLE"}else{if(c.multiSelect){b.mode="MULTI"}}}b=Ext.Factory.selection(Ext.apply({allowDeselect:c.allowDeselect,locked:a},b))}}return b},updateSelectionModel:function(a,c){var b=this;if(c){c.un({scope:b,lastselectedchanged:b.updateBindSelection,selectionchange:b.updateBindSelection});Ext.destroy(b.selModelRelayer)}b.selModelRelayer=b.relayEvents(a,["selectionchange","beforeselect","beforedeselect","select","deselect","focuschange"]);a.on({scope:b,lastselectedchanged:b.updateBindSelection,selectionchange:b.updateBindSelection});b.selModel=a},getVisibleColumnManager:function(){return this.ownerCt.getVisibleColumnManager()},getColumnManager:function(){return this.ownerCt.getColumnManager()},getTopLevelVisibleColumnManager:function(){return this.ownerGrid.getVisibleColumnManager()},moveColumn:function(a,u,k){var t=this,r=k>1,m=r&&document.createRange?document.createRange():null,b=r&&!m?document.createDocumentFragment():null,l=u,n=t.getGridColumns().length,s=n-1,e=(t.firstCls||t.lastCls)&&(u===0||u===n||a===0||a===s),p,o,h,q,c,d,g;if(t.rendered&&u!==a){h=t.el.query(t.rowSelector);for(p=0,q=h.length;p=(b-1)*a&&d.endIndex<=(b*a-1)){c.get(b);return false}},onViewScroll:function(b,a,c){if(!this.ignoreScroll){Ext.view.View.prototype.onViewScroll.call(this,b,a,c)}},createRowElement:function(b,c,d){var e=this,g=e.renderBuffer,a=e.collectData([b],c);a.columns=d;e.tpl.overwrite(g,a);e.cleanupData();return Ext.fly(g).down(e.getNodeContainerSelector(),true).firstChild},bufferRender:function(c,d){var e=this,g=e.renderBuffer,a,b=document.createRange?document.createRange():null;e.tpl.overwrite(g,e.collectData(c,d));e.cleanupData();Ext.fly(g).saveTabbableState({skipSelf:true,includeHidden:true});g=Ext.fly(g).down(e.getNodeContainerSelector(),true);if(b){b.selectNodeContents(g);a=b.extractContents()}else{a=document.createDocumentFragment();while(g.firstChild){a.appendChild(g.firstChild)}}return{fragment:a,children:Ext.Array.toArray(a.childNodes)}},collectData:function(a,c){var b=this;b.rowValues.view=b;b.tableValues.view=b;b.tableValues.rows=a;b.tableValues.columns=null;b.tableValues.viewStartIndex=c;b.tableValues.touchScroll=b.touchScroll;b.tableValues.tableStyle="width:"+b.headerCt.getTableWidth()+"px";return b.tableValues},cleanupData:function(){var a=this.tableValues;a.view=a.columns=a.rows=this.rowValues.view=null},refreshSize:function(c){var b=this,a=b.getBodySelector();if(a){b.body.attach(b.el.down(a,true))}if(!b.hasLoadingHeight){Ext.suspendLayouts();Ext.view.View.prototype.refreshSize.apply(this,arguments);if(c||(b.hasVariableRowHeight()&&b.dataSource.getCount())){b.grid.updateLayout()}Ext.resumeLayouts(true)}},clearViewEl:function(a){var e=this,g=e.all,h=e.getStore(),c,d,j,k=g.slice(),b=[];if(e.hasListeners.itemremove){for(c=g.startIndex;c<=g.endIndex;c++){b.push(h.getByInternalId(g.item(c,true).getAttribute("data-recordId")))}}e.fireEvent("itemremove",b,g.startIndex,k,e);Ext.view.View.prototype.clearViewEl.call(this);d=Ext.fly(e.getNodeContainer());if(d&&!a){j=e.getTargetEl();if(j.dom!==d.dom){d.destroy()}}},getMaskTarget:function(){return this.ownerCt.body},statics:{getBoundView:function(a){return Ext.getCmp(a.getAttribute("data-boundView"))}},getRecord:function(a){if(this.store.destroyed){return}if(a.isModel){return a}a=this.getNode(a);if(a){return this.dataSource.getByInternalId(a.getAttribute("data-recordId"))}},indexOf:function(a){a=this.getNode(a);if(!a&&a!==0){return -1}return this.all.indexOf(a)},indexInStore:function(a){return a?this.dataSource.indexOf(this.getRecord(a)):-1},indexOfRow:function(b){var c=this.dataSource,a;if(b.isCollapsedPlaceholder){a=c.indexOfPlaceholder(b)}else{a=c.indexOf(b)}return a},renderRows:function(h,e,d,b){var g=this,j=g.rowValues,a=h.length,c;j.view=g;j.columns=e;j.rowRole=g.rowAriaRole;g.cellValues.cellRole=g.cellAriaRole;for(c=0;c');for(d=0;d')}c.push("")},renderRow:function(h,a,g){var j=this,e=a===-1,i=j.selectionModel,m=j.rowValues,d=m.itemClasses,c=m.rowClasses,b=j.itemCls,l,k=j.rowTpl;m.rowAttr={};m.record=h;m.recordId=h.internalId;m.recordIndex=j.store.indexOf(h);m.rowIndex=a;m.rowId=j.getRowId(h);m.itemCls=m.rowCls="";if(!m.columns){m.columns=j.ownerCt.getVisibleColumnManager().getColumns()}d.length=c.length=0;if(!e){d[0]=b;if(!j.ownerCt.disableSelection&&i.isRowSelected){if(i.isRowSelected(h)){d.push(j.selectedItemCls)}}if(j.stripeRows&&a%2!==0){d.push(j.altRowCls)}if(j.getRowClass){l=j.getRowClass(h,a,null,j.dataSource);if(l){c.push(l)}}}if(g){k.applyOut(m,g,j.tableValues)}else{return k.apply(m,j.tableValues)}},renderCell:function(d,h,g,n,j,e){var l=this,b,i=l.selectionModel,k=l.cellValues,c=k.classes,a=h.data[d.dataIndex],o=l.cellTpl,p,m,q=l.navigationModel.getPosition();k.record=h;k.column=d;k.recordIndex=g;k.rowIndex=n;k.columnIndex=k.cellIndex=j;k.align=d.align;k.innerCls=d.innerCls;k.tdCls=k.tdStyle=k.tdAttr=k.style="";k.unselectableAttr=l.enableTextSelection?"":'unselectable="on"';c[1]=d.getCellId();m=2;if(d.renderer&&d.renderer.call){b=l.ownerCt.columnManager.getHeaderIndex(d);p=d.renderer.call(d.usingDefaultRenderer?d:d.scope||l.ownerCt,a,k,h,g,b,l.dataSource,l);if(k.css){h.cssWarning=true;k.tdCls+=" "+k.css;k.css=null}if(k.tdCls){c[m++]=k.tdCls}}else{p=a}k.value=(p==null||p==="")?d.emptyCellText:p;if(d.tdCls){c[m++]=d.tdCls}if(l.markDirty&&h.dirty&&h.isModified(d.dataIndex)){c[m++]=l.dirtyCls}if(d.isFirstVisible){c[m++]=l.firstCls}if(d.isLastVisible){c[m++]=l.lastCls}if(!l.enableTextSelection){c[m++]=l.unselectableCls}if(i&&(i.isCellModel||i.isSpreadsheetModel)&&i.isCellSelected(l,g,d)){c[m++]=l.selectedCellCls}if(q&&q.record.id===h.id&&q.column===d){c[m++]=l.focusedItemCls}c.length=m;k.tdCls=c.join(" ");o.applyOut(k,e);k.column=k.record=null},getRow:function(a){var b;if((!a&&a!==0)||!this.rendered){return null}if(a.target){a=a.target}if(Ext.isString(a)){return Ext.fly(a).down(this.rowSelector,true)}if(Ext.isNumber(a)){b=this.all.item(a);return b&&b.down(this.rowSelector,true)}if(a.isModel){return this.getRowByRecord(a)}b=Ext.fly(a);if(b.is(this.itemSelector)){return this.getRowFromItem(b)}return b.findParent(this.rowSelector,this.getTargetEl())},getRowId:function(a){return this.id+"-record-"+a.internalId},constructRowId:function(a){return this.id+"-record-"+a},getNodeById:function(a){a=this.constructRowId(a);return this.retrieveNode(a,false)},getRowById:function(a){a=this.constructRowId(a);return this.retrieveNode(a,true)},getNodeByRecord:function(a){return this.retrieveNode(this.getRowId(a),false)},getRowByRecord:function(a){return this.retrieveNode(this.getRowId(a),true)},getRowFromItem:function(c){var d=Ext.getDom(c).tBodies[0].childNodes,a=d.length,b;for(b=0;b1)){l=t._extData;k=K.createRowElement(h,K.indexOfRow(h),y);if(Ext.fly(t,"_internal").hasCls(x)){Ext.fly(k).addCls(x)}if(Ext.isIE9m&&t.mergeAttributes){t.mergeAttributes(k,true)}else{r=k.attributes;F=r.length;for(m=0;m0){if(Ext.supports.ScrollWidthInlinePaddingBug){h+=g.getCellPaddingAfter(l[0])}if(g.columnLines){h+=Ext.fly(l[0].parentNode).getBorderWidth("lr")}}a.setWidth(1);d.textEl.setStyle({"text-overflow":"clip",display:"table-cell"});k=d.textEl.dom.offsetWidth+d.titleEl.getPadding("lr");d.textEl.setStyle({"text-overflow":"",display:""});for(;ca){return false}if(e){k.view=e}k.setPosition(b,0)}else{k.navigate(+1)}break;case"left":if(h.isFirstColumn()){b=e&&g.isNormalView?b:b-1;if(b<0){return false}if(e){k.view=e;d=e.getVisibleColumnManager().getColumns()}k.setPosition(b,d[d.length-1])}else{k.navigate(-1)}break;case"up":if(b===0){return false}else{k.setRow(b-1)}break;case"down":if(b===a){return false}else{k.setRow(b+1)}break}if(c&&c.call(j||g,k)!==true){return false}return k},walkRows:function(i,a){var d=this,h=d.dataSource,e=0,k=i,b,c=(a<0)?0:h.getCount()-1,g=c?1:-1,j=i;do{if(c?j>=c:j<=c){return k||c}j+=g;if((b=Ext.fly(d.getRow(j)))&&b.isVisible(true)){e+=g;k=j}}while(e!==a);return j},walkRecs:function(b,a){var h=this,k=h.dataSource,i=0,l=b,c,e=(a<0)?0:(k.isBufferedStore?k.getTotalCount():k.getCount())-1,j=e?1:-1,g=k.indexOf(b),d;do{if(e?g>=e:g<=e){return l}g+=j;d=k.getAt(g);if(!d.isCollapsedPlaceholder&&(c=Ext.fly(h.getNodeByRecord(d)))&&c.isVisible(true)){i+=j;l=d}}while(i!==a);return l},getFirstVisibleRowIndex:function(){var c=this,b=(c.dataSource.isBufferedStore?c.dataSource.getTotalCount():c.dataSource.getCount()),a=c.indexOf(c.all.first())-1;do{a+=1;if(a===b){return}}while(!Ext.fly(c.getRow(a)).isVisible(true));return a},getLastVisibleRowIndex:function(){var b=this,a=b.indexOf(b.all.last());do{a-=1;if(a===-1){return}}while(!Ext.fly(b.getRow(a)).isVisible(true));return a},getHeaderCt:function(){return this.headerCt},getPosition:function(a,b){return new Ext.grid.CellContext(this).setPosition(a,b)},onDestroy:function(){var d=this,c=d.featuresMC,a,b;if(c){for(b=0,a=c.getCount();b=e.viewSize)){e.onReplace(b,c,[],a)}else{Ext.view.View.prototype.onAdd.apply(this,arguments)}d.setPendingStripe(c)},onRemove:function(b,a,c){var d=this,g=d.bufferedRenderer,e;if(d.rendered&&g&&d.dataSource.getCount()+a.length>=g.viewSize){e=d.saveFocusState();g.onReplace(b,c,a,[]);e()}else{Ext.view.View.prototype.onRemove.apply(this,arguments)}d.setPendingStripe(c)},saveFocusState:function(){var e=this,a=e.dataSource,h=e.actionableMode,c=e.getNavigationModel(),d=h?e.actionPosition:c.getPosition(true),b,g;if(d){d=d.clone();if(h){e.ownerGrid.setActionableMode(false)}e.el.dom.focus();c.setPosition();return function(){if(a.getCount()){b=Math.min(d.rowIdx,e.all.getCount()-1);g=Math.min(d.colIdx,e.getVisibleColumnManager().getColumns().length-1);d=new Ext.grid.CellContext(e).setPosition(a.contains(d.record)?d.record:b,g);if(h){e.ownerGrid.setActionableMode(true,d)}else{e.cellFocused=true;c.setPosition(d,null,null,null,true)}}else{d.column.focus()}}}return Ext.emptyFn},onDataRefresh:function(b){var c=this,a=c.ownerCt;if(a&&a.isCollapsingOrExpanding===2){a.on("expand",c.onDataRefresh,c,{single:true});return}Ext.view.View.prototype.onDataRefresh.call(this,b)},getViewRange:function(){var a=this;if(a.bufferedRenderer){return a.bufferedRenderer.getViewRange()}return Ext.view.View.prototype.getViewRange.call(this)},setPendingStripe:function(a){var b=this.stripeOnUpdate;if(b===null){b=a}else{b=Math.min(b,a)}this.stripeOnUpdate=b},onEndUpdate:function(){var a=this,c=a.stripeOnUpdate,b=a.all.startIndex;if(a.rendered&&(c||c===0)){if(c-1)&&!p;e=e+(h?1:-1)){b=c[e];g.setColumn(b);s=Ext.fly(a).down(g.column.getCellSelector());for(d=0;da;e=b?1:0;d=c.getGridColumns();for(g=0,k=d.length;g=0&&c<=d.view.dataSource.getCount()){if(b){g=d.down(":focusable:not([isButton]):first");a.column.getView().getScrollable().scrollIntoView(a.ownerCt.child(":focusable").el)}else{g=d.down(":focusable:not([isButton]):last")}d.editingPlugin.startEdit(c,g.column)}}}},destroyColumnEditor:function(a){var b;if(a.hasEditor()&&(b=a.getEditor())){b.destroy()}},getFloatingButtons:function(){var b=this,a=b.floatingButtons;if(!a){b.floatingButtons=a=new Ext.grid.RowEditorButtons({ownerCmp:b,rowEditor:b})}return a},repositionIfVisible:function(d){var b=this,a=b.view;if(d&&(d===b||!d.el.isAncestor(a.el))){return}if(b.isVisible()&&a.isVisible(true)){b.reposition()}},isLayoutChild:function(a){return false},getRefOwner:function(){return this.editingPlugin.grid},getRefItems:function(b){var c=this,a;if(c.lockable){a=[c.lockedColumnContainer];a.push.apply(a,c.lockedColumnContainer.getRefItems(b));a.push(c.normalColumnContainer);a.push.apply(a,c.normalColumnContainer.getRefItems(b))}else{a=Ext.form.Panel.prototype.getRefItems.apply(this,arguments)}a.push.apply(a,c.getFloatingButtons().getRefItems(b));return a},reposition:function(i,e){var g=this,b=g.context,k=b&&b.row,j=g.wrapEl,a,c,d,h;if(k&&Ext.isElement(k)){d=g.syncButtonPosition(g.getScrollDelta());a=g.calculateLocalRowTop(k);c=g.calculateEditorTop(a);if(!e){h=function(){if(d){g.scrollingViewEl.scrollBy(0,d,true)}g.focusColumnField(b.column)}}g.syncEditorClip();if(i){j.animate(Ext.applyIf({to:{top:c},duration:i.duration||125,callback:h},i))}else{j.setLocalY(c);if(h){h()}}}},getScrollDelta:function(){var e=this,d=e.scrollingViewEl.dom,c=e.context,b=e.body,a=0;if(c){a=Ext.fly(c.row).getOffsetsTo(d)[1];if(a<0){a-=b.getBorderPadding().beforeY}else{if(a>0){a=Math.max(a+e.getHeight()+e.floatingButtons.getHeight()-d.clientHeight-b.getBorderWidth("b"),0);if(a>0){a-=b.getBorderPadding().afterY}}}}return a},calculateLocalRowTop:function(b){var a=this.editingPlugin.grid;return Ext.fly(b).getOffsetsTo(a)[1]-a.el.getBorderWidth("t")+this.lastScrollTop},calculateEditorTop:function(a){return a-this.body.getBorderPadding().beforeY-this.lastScrollTop},getClientWidth:function(){var c=this,b=c.editingPlugin.grid,a;if(c.lockable){a=b.lockedGrid.getWidth()+b.normalGrid.view.el.dom.clientWidth}else{a=b.view.el.dom.clientWidth}return a},getEditor:function(a){var b=this;if(Ext.isNumber(a)){return b.query("[isEditorComponent]")[a]}else{if(a.isHeader&&!a.isGroupHeader){return a.getEditor()}}},addFieldsForColumn:function(c,a){var e=this,b,d,g;if(Ext.isArray(c)){for(b=0,d=c.length;bdisplayfield");b=g.length;for(c=0;c0){if(!b._buttonsOnTop){a.setButtonPosition("top");b._buttonsOnTop=true}c=0}else{if(b._buttonsOnTop!==false){a.setButtonPosition("bottom");b._buttonsOnTop=false}else{a.setButtonPosition(a.position)}}return c},syncEditorClip:function(){var i=this,b=i.getScrollDelta(),a=i.el,e=i.floatingButtons,d=e.el,j=Math.max,g,c,h;if(b){i.isOverflowing=true;g=i.body;c=e.getHeight();h=i.getHeight();j=Math.max;if(b>0){if(i._buttonsOnTop){b-=(c-g.getBorderWidth("b"));i.clipBottom(a,j(h-b),0);b-=(h-g.getBorderWidth("t"));if(b>0){i.clipBottom(d,j(c-b,0))}else{i.clearClip(d)}}else{i.clipBottom(d,j(c-b,0));b-=(c-g.getBorderWidth("b"));if(b>0){i.clipBottom(a,j(h-b,0))}else{i.clearClip(a)}}}else{if(b<0){b=Math.abs(b);i.clipTop(a,b);b-=(h-g.getBorderWidth("b"));if(b>0){i.clipTop(d,b)}else{i.clearClip(d)}}}}else{if(i.isOverflowing){i.clearClip(d);i.clearClip(a);i.isOverflowing=false}}},focusColumnField:function(b){var c,a;if(b&&!b.destroyed){if(b.isVisible()){c=this.getEditor(b);if(c&&c.isFocusable(true)){a=true;c.focus()}}if(!a){this.focusColumnField(b.next())}}},cancelEdit:function(){var g=this,e=g.getForm(),a=e.getFields(),b=a.items,d=b.length,c;if(g._cachedNode){g.clearCache()}g.hide();e.clearInvalid();for(c=0;cg&&a[isFormField]"),d=b.length,c,a,g;for(c=0;c'+h.join("")+""},createErrorListItem:function(b,a){b=a?a+": "+b:b;return'
  • '+b+"
  • "},beforeDestroy:function(){Ext.destroy(this.floatingButtons,this.tooltip);Ext.form.Panel.prototype.beforeDestroy.call(this)},clipBottom:function(a,b){a.setStyle("clip","rect(0 auto "+b+"px 0)")},clipTop:function(a,b){a.setStyle("clip","rect("+b+"px, auto, auto, 0)")},clearClip:function(a){a.setStyle("clip",Ext.isIE8?"rect(-1000px auto 1000px auto)":"auto")}},0,["roweditor"],["component","box","container","panel","form","roweditor"],{component:true,box:true,container:true,panel:true,form:true,roweditor:true},["widget.roweditor"],0,[Ext.grid,"RowEditor"],0));(Ext.cmd.derive("Ext.view.DropZone",Ext.dd.DropZone,{indicatorCls:"x-grid-drop-indicator",indicatorHtml:['',''].join(""),constructor:function(a){var b=this;Ext.apply(b,a);if(!b.ddGroup){b.ddGroup="view-dd-zone-"+b.view.id}Ext.dd.DropZone.prototype.constructor.call(this,b.view.el)},fireViewEvent:function(){var b=this,a;b.lock();a=b.view.fireEvent.apply(b.view,arguments);b.unlock();return a},getTargetFromEvent:function(k){var j=k.getTarget(this.view.getItemSelector()),d,c,b,g,a,h;if(!j){d=k.getY();for(g=0,c=this.view.getNodes(),a=c.length;g=(b.bottom-b.top)/2){d="before"}else{d="after"}return d},containsRecordAtOffset:function(d,b,g){if(!b){return false}var a=this.view,c=a.indexOf(b),e=a.getNode(c+g),h=e?a.getRecord(e):null;return h&&Ext.Array.contains(d,h)},positionIndicator:function(b,c,d){var g=this,i=g.view,h=g.getPosition(d,b),k=i.getRecord(b),a=c.records,j;if(!Ext.Array.contains(a,k)&&(h==="before"&&!g.containsRecordAtOffset(a,k,-1)||h==="after"&&!g.containsRecordAtOffset(a,k,1))){g.valid=true;if(g.overRecord!==k||g.currentPosition!==h){j=Ext.fly(b).getY()-i.el.getY()-1;if(h==="after"){j+=Ext.fly(b).getHeight()}if(i.touchScroll===2){j+=i.getScrollY()}g.getIndicator().setWidth(Ext.fly(i.el).getWidth()).showAt(0,j);g.overRecord=k;g.currentPosition=h}}else{g.invalidateDrop()}},invalidateDrop:function(){if(this.valid){this.valid=false;this.getIndicator().hide()}},onNodeOver:function(c,a,g,d){var b=this;if(!Ext.Array.contains(d.records,b.view.getRecord(c))){b.positionIndicator(c,d,g)}return b.valid?b.dropAllowed:b.dropNotAllowed},notifyOut:function(c,a,g,d){var b=this;Ext.dd.DropZone.prototype.notifyOut.apply(this,arguments);b.overRecord=b.currentPosition=null;b.valid=false;if(b.indicator){b.indicator.hide()}},onContainerOver:function(a,h,g){var d=this,b=d.view,c=b.dataSource.getCount();if(c){d.positionIndicator(b.all.last(),g,h)}else{d.overRecord=d.currentPosition=null;d.getIndicator().setWidth(Ext.fly(b.el).getWidth()).showAt(0,0);d.valid=true}return d.dropAllowed},onContainerDrop:function(a,c,b){return this.onNodeDrop(a,null,c,b)},onNodeDrop:function(i,a,h,g){var d=this,c=false,b={wait:false,processDrop:function(){d.invalidateDrop();d.handleNodeDrop(g,d.overRecord,d.currentPosition);c=true;d.fireViewEvent("drop",i,g,d.overRecord,d.currentPosition)},cancelDrop:function(){d.invalidateDrop();c=true}},j=false;if(d.valid){j=d.fireViewEvent("beforedrop",i,g,d.overRecord,d.currentPosition,b);if(b.wait){return}if(j!==false){if(!c){b.processDrop()}}}return j},destroy:function(){this.indicator=Ext.destroy(this.indicator);Ext.dd.DropZone.prototype.destroy.call(this)}},1,0,0,0,0,0,[Ext.view,"DropZone"],0));(Ext.cmd.derive("Ext.grid.ViewDropZone",Ext.view.DropZone,{indicatorHtml:'',indicatorCls:"x-grid-drop-indicator",handleNodeDrop:function(b,d,e){var j=this.view,k=j.getStore(),h,a,c,g;if(b.copy){a=b.records;b.records=[];for(c=0,g=a.length;c=0&&!(j.isGroupHeader&&(!j.items||!j.items.length))&&o!==b){k=n.isGroupHeader?n.query(":not([hidden]):not([isGroupHeader])").length:1;if((o<=b)&&k>1){b-=k}a.getRootHeaderCt().grid.view.moveColumn(o,b,k)}l.fireEvent("columnmove",t,n,o,b);t.isDDMoveInGrid=a.isDDMoveInGrid=false;if(a.isGroupHeader&&!t.isGroupHeader){if(t!==a){n.savedFlex=n.flex;delete n.flex;n.width=g}}else{if(!t.isGroupHeader){if(n.savedFlex){n.flex=n.savedFlex;delete n.width}}}Ext.resumeLayouts(true)}}},1,0,0,0,0,0,[Ext.grid.header,"DropZone"],0));(Ext.cmd.derive("Ext.grid.plugin.HeaderReorderer",Ext.plugin.Abstract,{init:function(a){this.headerCt=a;a.on({boxready:this.onHeaderCtRender,single:true,scope:this})},destroy:function(){var a=this;a.headerCt.un("boxready",a.onHeaderCtRender,a);Ext.destroy(a.dragZone,a.dropZone);a.headerCt=a.dragZone=a.dropZone=null;Ext.plugin.Abstract.prototype.destroy.call(this)},onHeaderCtRender:function(){var a=this;a.dragZone=new Ext.grid.header.DragZone(a.headerCt);a.dropZone=new Ext.grid.header.DropZone(a.headerCt);if(a.disabled){a.dragZone.disable()}},enable:function(){this.disabled=false;if(this.dragZone){this.dragZone.enable()}},disable:function(){this.disabled=true;if(this.dragZone){this.dragZone.disable()}}},0,0,0,0,["plugin.gridheaderreorderer"],0,[Ext.grid.plugin,"HeaderReorderer"],0));(Ext.cmd.derive("Ext.grid.header.Container",Ext.container.Container,{border:true,baseCls:"x-grid-header-ct",dock:"top",weight:100,defaultType:"gridcolumn",detachOnRemove:false,defaultWidth:100,sortAscText:"Sort Ascending",sortDescText:"Sort Descending",sortClearText:"Clear Sort",columnsText:"Columns",headerOpenCls:"x-column-header-open",menuSortAscCls:"x-hmenu-sort-asc",menuSortDescCls:"x-hmenu-sort-desc",menuColsIcon:"x-cols-icon",blockEvents:false,dragging:false,sortOnClick:true,enableFocusableContainer:false,childHideCount:0,sortable:true,enableColumnHide:true,initComponent:function(){var a=this;a.plugins=a.plugins||[];a.defaults=a.defaults||{};if(!a.isColumn){if(a.enableColumnResize){a.resizer=new Ext.grid.plugin.HeaderResizer();a.plugins.push(a.resizer)}if(a.enableColumnMove){a.reorderer=new Ext.grid.plugin.HeaderReorderer();a.plugins.push(a.reorderer)}}if(a.isColumn&&!a.isGroupHeader){if(!a.items||a.items.length===0){a.isContainer=a.isFocusableContainer=false;a.focusable=true;a.layout={type:"container",calculate:Ext.emptyFn}}}else{a.layout=Ext.apply({type:"gridcolumn",align:"stretch"},a.initialConfig.layout);a.defaults.columnLines=a.columnLines;if(!a.isGroupHeader){a.isRootHeader=true;if(!a.hiddenHeaders){a.enableFocusableContainer=true;a.ariaRole="rowgroup"}a.columnManager=new Ext.grid.ColumnManager(false,a);a.visibleColumnManager=new Ext.grid.ColumnManager(true,a);if(a.grid){a.grid.columnManager=a.columnManager;a.grid.visibleColumnManager=a.visibleColumnManager}}else{a.visibleColumnManager=new Ext.grid.ColumnManager(true,a);a.columnManager=new Ext.grid.ColumnManager(false,a)}}a.menuTask=new Ext.util.DelayedTask(a.updateMenuDisabledState,a);Ext.container.Container.prototype.initComponent.call(this)},insertNestedHeader:function(g){var b=this,e=g.ownerCt,a=b.ownerCt,d=a.layout.owner,c;if(e){if(b.isGroupHeader&&!a.isNestedParent){c=d.items.indexOf(b)}e.remove(g,false)}if(c===undefined){c=d.items.indexOf(b)}d.insert(c,g)},isNested:function(){return !!this.getRootHeaderCt().down("[isNestedParent]")},isNestedGroupHeader:function(){var b=this,a=b.getRefOwner().query(">:not([hidden])");return(a.length===1&&a[0]===b)},maybeShowNestedGroupHeader:function(){var a=this.items,b;if(a&&a.length===1&&(b=a.getAt(0))&&b.hidden){b.show()}},setNestedParent:function(a){a.isNestedParent=false;a.ownerCt.isNestedParent=!!(this.ownerCt.items.length===1&&a.ownerCt.items.length===1)},initEvents:function(){var c=this,a,b;Ext.container.Container.prototype.initEvents.call(this);if(!c.isColumn&&!c.isGroupHeader){a=c.onHeaderCtEvent;b={click:a,dblclick:a,contextmenu:a,mouseover:c.onHeaderCtMouseOver,mouseout:c.onHeaderCtMouseOut,scope:c};if(Ext.supports.Touch){b.longpress=c.onHeaderCtLongPress}c.mon(c.el,b)}},onHeaderCtEvent:function(d,b){var c=this,i=c.getHeaderElByEvent(d),h,g,a;if(c.longPressFired){c.longPressFired=false;return}if(i&&!c.blockEvents){h=Ext.getCmp(i.id);if(h){g=h[h.clickTargetName];if((!h.isGroupHeader&&!h.isContainer)||d.within(g)){if(d.type==="click"||d.type==="tap"){a=h.onTitleElClick(d,g,c.sortOnClick);if(a){c.onHeaderTriggerClick(a,d,d.pointerType==="touch"?a.el:a.triggerEl)}else{c.onHeaderClick(h,d,b)}}else{if(d.type==="contextmenu"){c.onHeaderContextMenu(h,d,b)}else{if(d.type==="dblclick"&&h.resizable){h.onTitleElDblClick(d,g.dom)}}}}}}},blockNextEvent:function(){this.blockEvents=true;Ext.asap(this.unblockEvents,this)},unblockEvents:function(){this.blockEvents=false},onHeaderCtMouseOver:function(b,a){var g,d,c;if(!b.within(this.el,true)){g=b.getTarget("."+Ext.grid.column.Column.prototype.baseCls);d=g&&Ext.getCmp(g.id);if(d){c=d[d.clickTargetName];if(b.within(c)){d.onTitleMouseOver(b,c.dom)}}}},onHeaderCtMouseOut:function(g,c){var d="."+Ext.grid.column.Column.prototype.baseCls,b=g.getTarget(d),a=g.getRelatedTarget(d),i,h;if(b!==a){if(b){i=Ext.getCmp(b.id);if(i){h=i[i.clickTargetName];i.onTitleMouseOut(g,h.dom)}}if(a){i=Ext.getCmp(a.id);if(i){h=i[i.clickTargetName];i.onTitleMouseOver(g,h.dom)}}}},onHeaderCtLongPress:function(b){var a=this,d=a.getHeaderElByEvent(b),c=Ext.getCmp(d.id);if(!c.menuDisabled){a.longPressFired=true;a.showMenuBy(b,d,c)}},getHeaderElByEvent:function(a){return a.getTarget("."+Ext.grid.column.Column.prototype.baseCls)},isLayoutRoot:function(){if(this.hiddenHeaders){return false}return Ext.container.Container.prototype.isLayoutRoot.call(this)},getRootHeaderCt:function(){var a=this;return a.isRootHeader?a:a.up("[isRootHeader]")},onDestroy:function(){var a=this;if(a.menu){a.menu.un("hide",a.onMenuHide,a)}a.menuTask.cancel();Ext.container.Container.prototype.onDestroy.call(this);Ext.destroy(a.visibleColumnManager,a.columnManager,a.menu);a.columnManager=a.visibleColumnManager=null},applyColumnsState:function(j,e){if(!j||!j.length){return}var q=this,o=q.items.items,n=o.length,k=0,b=j.length,p,d,a,m,r=false,l=[],g={},h=[];for(p=0;p=a.visibleFromIdx){c++}Ext.container.Container.prototype.onMove.apply(this,arguments);if(a.isGroupHeader){i=a.visibleColumnManager.getColumns().length}e.onHeaderMoved(a,i,a.visibleFromIdx,c)},maybeContinueRemove:function(){var a=this;return(a.isGroupHeader&&!a.applyingState)&&!a.isNestedParent&&a.ownerCt&&!a.items.getCount()},onRemove:function(g,d){var e=this,b=e.ownerCt,a=g.lastHiddenHeader;Ext.container.Container.prototype.onRemove.call(this,g,d);if(!e.destroying){if(!e.isDDMoveInGrid){e.onHeadersChanged(g,false)}if(e.maybeContinueRemove()){if(g.rendered){e.detachComponent(g)}Ext.suspendLayouts();b.remove(e);Ext.resumeLayouts(true)}}},onHeadersChanged:function(e,a){var b,d=this.getRootHeaderCt();this.purgeHeaderCtCache(this);if(d){d.onColumnsChanged();if(!e.isGroupHeader){b=d.ownerCt;if(b&&!a){b.onHeadersChanged(d,e)}}}},onHeaderMoved:function(g,a,c,e){var d=this,b=d.ownerCt;if(d.rendered){if(b&&b.onHeaderMove){b.onHeaderMove(d,g,a,c,e)}d.fireEvent("columnmove",d,g,c,e)}},onColumnsChanged:function(){var c=this,d=c.menu,a,b;if(c.rendered){c.fireEvent("columnschanged",c);if(d&&(a=d.child("#columnItemSeparator"))){b=d.child("#columnItem");a.destroy();b.destroy()}}},lookupComponent:function(b){var a=Ext.container.Container.prototype.lookupComponent.apply(this,arguments);if(!a.isGroupHeader&&a.width===undefined&&!a.flex){a.width=this.defaultWidth}return a},setSortState:function(){var b=this.up("[store]").store,d=this.visibleColumnManager.getColumns(),a=d.length,c,g,e;for(c=0;cgridcolumn[hideable]"),h=a.length,d;for(;bo.el.dom.clientHeight?Ext.getScrollbarSize().width:0),e=0,m=n.getVisibleGridColumns(),j=h.hidden,l,g,r,k,c;function p(){for(g=0,l=m.length;gk){h.width=k;d=true}else{h.width=c;a-=c+s;p()}q();Ext.resumeLayouts(true)},autoSizeColumn:function(b){var a=this.view;if(a){a.autoSizeColumn(b);if(this.forceFit){this.applyForceFit(b)}}},getRefItems:function(b){var a=Ext.container.Container.prototype.getRefItems.call(this,b);if(this.menu){a.push(this.menu)}return a},privates:{beginChildHide:function(){++this.childHideCount},endChildHide:function(){--this.childHideCount},getFocusables:function(){return this.isRootHeader?this.getVisibleGridColumns():this.items.items},createFocusableContainerKeyNav:function(a){var b=this;return new Ext.util.KeyNav(a,{scope:b,down:b.showHeaderMenu,left:b.onFocusableContainerLeftKey,right:b.onFocusableContainerRightKey,home:b.onHomeKey,end:b.onEndKey,space:b.onHeaderActivate,enter:b.onHeaderActivate})},onHomeKey:function(a){return this.focusChild(null,true,a)},onEndKey:function(a){return this.focusChild(null,false,a)},showHeaderMenu:function(b){var a=this.getFocusableFromEvent(b);if(a&&a.isColumn&&a.triggerEl){this.onHeaderTriggerClick(a,b,a.triggerEl)}},onHeaderActivate:function(d){var c=this.getFocusableFromEvent(d),a,b;if(c&&c.isColumn){a=c.getView();if(c.sortable&&this.sortOnClick){b=a.getNavigationModel().getLastFocused();c.toggleSortState();if(b){a.ownerCt.ensureVisible(b.record)}}this.onHeaderClick(c,d,c.el)}},onFocusableContainerMousedown:function(c,b){var a=Ext.Component.fromElement(b);if(a===this){c.preventDefault()}else{a.focus()}}}},0,["headercontainer"],["component","box","container","headercontainer"],{component:true,box:true,container:true,headercontainer:true},["widget.headercontainer"],[[Ext.util.FocusableContainer.prototype.mixinId||Ext.util.FocusableContainer.$className,Ext.util.FocusableContainer]],[Ext.grid.header,"Container"],0));(Ext.cmd.derive("Ext.grid.column.Column",Ext.grid.header.Container,{alternateClassName:"Ext.grid.Column",config:{triggerVisible:false,sorter:null},baseCls:"x-column-header",hoverCls:"x-column-header-over",ariaRole:"columnheader",enableFocusableContainer:false,sortState:null,possibleSortStates:["ASC","DESC"],ariaSortStates:{ASC:"ascending",DESC:"descending"},childEls:["titleEl","triggerEl","textEl","textContainerEl"],headerWrap:false,renderTpl:['","{%this.renderContainer(out,values)%}"],dataIndex:null,text:" ",menuText:null,emptyCellText:" ",sortable:true,resizable:true,hideable:true,menuDisabled:false,renderer:false,align:"left",draggable:true,tooltipType:"qtip",initDraggable:Ext.emptyFn,tdCls:"",producesHTML:true,ignoreExport:false,isHeader:true,isColumn:true,tabIndex:-1,ascSortCls:"x-column-header-sort-ASC",descSortCls:"x-column-header-sort-DESC",componentLayout:"columncomponent",groupSubHeaderCls:"x-group-sub-header",groupHeaderCls:"x-group-header",clickTargetName:"titleEl",detachOnRemove:true,initResizable:Ext.emptyFn,rendererNames:{column:"renderer",edit:"editRenderer",summary:"summaryRenderer"},formatterNames:{column:"formatter",edit:"editFormatter",summary:"summaryFormatter"},initComponent:function(){var a=this;if(!a.rendererScope){a.rendererScope=a.scope}if(a.header!=null){a.text=a.header;a.header=null}if(a.cellWrap){a.tdCls=(a.tdCls||"")+" x-wrap-cell"}if(a.columns!=null){a.isGroupHeader=true;a.ariaRole="presentation";a.items=a.columns;a.columns=a.flex=a.width=null;a.cls=(a.cls||"")+" "+a.groupHeaderCls;a.sortable=a.resizable=false;a.align="center"}else{if(a.flex){a.minWidth=a.minWidth||Ext.grid.plugin.HeaderResizer.prototype.minColWidth}}a.addCls("x-column-header-align-"+a.align);a.setupRenderer();a.setupRenderer("edit");a.setupRenderer("summary");Ext.grid.header.Container.prototype.initComponent.apply(this,arguments)},onAdded:function(b,h,a){var c=this,g,d,e;Ext.grid.header.Container.prototype.onAdded.call(this,b,h,a);if(!c.headerId){d=c.up("tablepanel");e=d?d.ownerGrid:c.getRootHeaderCt();e.headerCounter=(e.headerCounter||0)+1;c.headerId="h"+e.headerCounter}if(!c.stateId){c.stateId=c.initialConfig.id||c.headerId}g=c.getSorter();if(g&&!g.initialConfig.id){g.setId((c.dataIndex||c.stateId)+"-sorter")}},applySorter:function(a){return this.getRootHeaderCt().up("tablepanel").store.getData().getSorters().decodeSorter(a)},bindFormatter:function(b){var a=this;return function(c){return b.format(c,b.scope||a.rendererScope||a.resolveListenerScope())}},bindRenderer:function(b){var a=this;a.hasCustomRenderer=true;return function(){return Ext.callback(b,a.rendererScope,arguments,0,a)}},setupRenderer:function(b){b=b||"column";var c=this,g=c[c.formatterNames[b]],d=c[c.rendererNames[b]],a=b==="column",h,e;if(!g){if(d){if(typeof d==="string"){d=c[c.rendererNames[b]]=c.bindRenderer(d);e=true}if(a){c.hasCustomRenderer=e||d.length>1}}else{if(a&&c.defaultRenderer){c.renderer=c.defaultRenderer;c.usingDefaultRenderer=true}}}else{h=g.indexOf("this.")===0;if(h){g=g.substring(5)}g=Ext.app.bind.Template.prototype.parseFormat(g);c[c.formatterNames[b]]=null;if(h){g.scope=null}c[c.rendererNames[b]]=c.bindFormatter(g)}},getView:function(){var a=this.getRootHeaderCt();if(a){return a.view}},onFocusLeave:function(a){Ext.grid.header.Container.prototype.onFocusLeave.call(this,a);if(this.activeMenu){this.activeMenu.hide()}},initItems:function(){var a=this;Ext.grid.header.Container.prototype.initItems.apply(this,arguments);if(a.isGroupHeader){if(a.config.hidden||!a.hasVisibleChildColumns()){a.hide()}}},hasVisibleChildColumns:function(){var b=this.items.items,a=b.length,c,d;for(c=0;cgridcolumn:not([hidden]):not([menuDisabled])");c=b.length;if(Ext.Array.contains(b,a.hideCandidate)){c--}if(c){return false}a.hideCandidate=this},isLockable:function(){var a={result:this.lockable!==false};if(a.result){this.ownerCt.bubble(this.hasMultipleVisibleChildren,null,[a])}return a.result},isLocked:function(){return this.locked||!!this.up("[isColumn][locked]","[isRootHeader]")},hasMultipleVisibleChildren:function(a){if(!this.isXType("headercontainer")){a.result=false;return false}if(this.query(">gridcolumn:not([hidden])").length>1){return false}},hide:function(){var c=this,b=c.getRootHeaderCt(),a=c.getRefOwner();if(a.constructing){Ext.grid.header.Container.prototype.hide.call(this);return c}if(c.rendered&&!c.isVisible()){return c}if(b.forceFit){c.visibleSiblingCount=b.getVisibleGridColumns().length-1;if(c.flex){c.savedWidth=c.getWidth();c.flex=null}}b.beginChildHide();Ext.suspendLayouts();if(a.isGroupHeader){if(c.isNestedGroupHeader()){a.hide()}if(c.isSubHeader&&!c.isGroupHeader&&a.query(">gridcolumn:not([hidden])").length===1){a.lastHiddenHeader=c}}Ext.grid.header.Container.prototype.hide.call(this);b.endChildHide();b.onHeaderHide(c);Ext.resumeLayouts(true);return c},show:function(){var c=this,a=c.getRootHeaderCt(),b=c.getRefOwner();if(c.isVisible()){return c}if(b.isGroupHeader){b.lastHiddenHeader=null}if(c.rendered){if(a.forceFit){a.applyForceFit(c)}}Ext.suspendLayouts();if(c.isSubHeader&&b.hidden){b.show(false,true)}Ext.grid.header.Container.prototype.show.apply(this,arguments);if(c.isGroupHeader){c.maybeShowNestedGroupHeader()}b=c.getRootHeaderCt();if(b){b.onHeaderShow(c)}Ext.resumeLayouts(true);return c},shouldUpdateCell:function(b,d){if(!this.preventUpdate){if(this.hasCustomRenderer){return 1}if(d){var a=d.length,c,e;for(c=0;cActions",ignoreExport:true,sortable:false,innerCls:"x-grid-cell-inner-action-col",actionIconCls:"x-action-col-icon",constructor:function(d){var g=this,b=Ext.apply({},d),c=b.items||g.items||[g],h,e,a;g.origRenderer=b.renderer||g.renderer;g.origScope=b.scope||g.scope;g.renderer=g.scope=b.renderer=b.scope=null;b.items=null;Ext.grid.column.Column.prototype.constructor.call(this,b);g.items=c;for(e=0,a=c.length;e":">")}return t},updater:function(a,e,c,b,g){var d={};Ext.fly(a).addCls(d.tdCls).down(this.getView().innerSelector,true).innerHTML=this.defaultRenderer(e,d,c,null,null,g,b)},enableAction:function(b,a){var c=this;if(!b){b=0}else{if(!Ext.isNumber(b)){b=Ext.Array.indexOf(c.items,b)}}c.items[b].disabled=false;c.up("tablepanel").el.select(".x-action-col-"+b).removeCls(c.disabledCls);if(!a){c.fireEvent("enable",c)}},disableAction:function(b,a){var c=this;if(!b){b=0}else{if(!Ext.isNumber(b)){b=Ext.Array.indexOf(c.items,b)}}c.items[b].disabled=true;c.up("tablepanel").el.select(".x-action-col-"+b).addCls(c.disabledCls);if(!a){c.fireEvent("disable",c)}},beforeDestroy:function(){this.renderer=this.items=null;return Ext.grid.column.Column.prototype.beforeDestroy.apply(this,arguments)},processEvent:function(k,m,o,b,l,i,d,q){var j=this,h=i.getTarget(),n=k==="keydown"&&i.getKey(),c,p,a,g=Ext.fly(o);i.stopSelection=!n&&j.stopSelection;if(n&&(h===o||!g.contains(h))){h=g.query("."+j.actionIconCls,true);if(h.length===1){h=h[0]}else{return}}if(h&&(c=h.className.match(j.actionIdRe))){p=j.items[parseInt(c[1],10)];a=p.disabled||(p.isDisabled?p.isDisabled.call(p.scope||j.origScope||j,m,b,l,p,d):false);if(p&&!a){if(k==="mousedown"&&!j.getView().actionableMode){i.preventDefault()}else{if(k==="click"||(n===i.ENTER||n===i.SPACE)){Ext.callback(p.handler||j.handler,p.scope||j.origScope,[m,b,l,p,i,d,q],undefined,j);if(!m.el.contains(Ext.Element.getActiveElement())){return false}}}}}return Ext.grid.column.Column.prototype.processEvent.apply(this,arguments)},cascade:function(b,a){b.call(a||this,this)},getRefItems:function(){return[]},privates:{getFocusables:function(){return[]},shouldUpdateCell:function(){return 2}}},1,["actioncolumn"],["component","box","container","headercontainer","gridcolumn","actioncolumn"],{component:true,box:true,container:true,headercontainer:true,gridcolumn:true,actioncolumn:true},["widget.actioncolumn"],0,[Ext.grid.column,"Action",Ext.grid,"ActionColumn"],0));(Ext.cmd.derive("Ext.grid.column.Boolean",Ext.grid.column.Column,{alternateClassName:"Ext.grid.BooleanColumn",trueText:"true",falseText:"false",undefinedText:" ",defaultFilterType:"boolean",producesHTML:false,defaultRenderer:function(a){if(a===undefined){return this.undefinedText}if(!a||a==="false"){return this.falseText}return this.trueText},updater:function(a,b){Ext.fly(a).down(this.getView().innerSelector,true).innerHTML=Ext.grid.column.Boolean.prototype.defaultRenderer.call(this,b)}},0,["booleancolumn"],["component","box","container","headercontainer","gridcolumn","booleancolumn"],{component:true,box:true,container:true,headercontainer:true,gridcolumn:true,booleancolumn:true},["widget.booleancolumn"],0,[Ext.grid.column,"Boolean",Ext.grid,"BooleanColumn"],0));(Ext.cmd.derive("Ext.grid.column.Check",Ext.grid.column.Column,{alternateClassName:["Ext.ux.CheckColumn","Ext.grid.column.CheckColumn"],align:"center",ignoreExport:true,stopSelection:true,tdCls:"x-grid-cell-checkcolumn",innerCls:"x-grid-cell-inner-checkcolumn",clickTargetName:"el",defaultFilterType:"boolean",constructor:function(){this.scope=this;Ext.grid.column.Column.prototype.constructor.apply(this,arguments)},processEvent:function(j,l,o,c,k,h,d,p){var i=this,n=j==="keydown"&&h.getKey(),a=j==="mousedown",b=i.disabled,g,m;h.stopSelection=!n&&i.stopSelection;if(!b&&(a||(n===h.ENTER||n===h.SPACE))){m=!i.isRecordChecked(d);if(i.fireEvent("beforecheckchange",i,c,m)!==false){i.setRecordCheck(d,m,o,p,h);i.fireEvent("checkchange",i,c,m);if(a&&!i.getView().actionableMode){h.preventDefault()}}}else{g=Ext.grid.column.Column.prototype.processEvent.apply(this,arguments)}return g},onEnable:function(){Ext.grid.column.Column.prototype.onEnable.apply(this,arguments);this._setDisabled(false)},onDisable:function(){this._setDisabled(true)},_setDisabled:function(c){var d=this,a=d.disabledCls,b;b=d.up("tablepanel").el.select(d.getCellSelector());if(c){b.addCls(a)}else{b.removeCls(a)}},defaultRenderer:function(c,b){var d="x-",a=d+"grid-checkcolumn";if(this.disabled){b.tdCls+=" "+this.disabledCls}if(c){a+=" "+d+"grid-checkcolumn-checked"}return'
    '},isRecordChecked:function(a){var b=this.property;if(b){return a[b]}return a.get(this.dataIndex)},setRecordCheck:function(b,d,a,h,g){var c=this,i=c.property;if(i){b[i]=d;c.updater(a,d)}else{b.set(c.dataIndex,d)}},updater:function(a,b){a=Ext.fly(a);a[this.disabled?"addCls":"removeCls"](this.disabledCls);Ext.fly(a.down(this.getView().innerSelector,true).firstChild)[b?"addCls":"removeCls"]("x-grid-checkcolumn-checked")}},1,["checkcolumn"],["component","box","container","headercontainer","gridcolumn","checkcolumn"],{component:true,box:true,container:true,headercontainer:true,gridcolumn:true,checkcolumn:true},["widget.checkcolumn"],0,[Ext.grid.column,"Check",Ext.ux,"CheckColumn",Ext.grid.column,"CheckColumn"],0));(Ext.cmd.derive("Ext.grid.column.Date",Ext.grid.column.Column,{alternateClassName:"Ext.grid.DateColumn",isDateColumn:true,defaultFilterType:"date",producesHTML:false,initComponent:function(){if(!this.format){this.format=Ext.Date.defaultFormat}Ext.grid.column.Column.prototype.initComponent.apply(this,arguments)},defaultRenderer:function(a){return Ext.util.Format.date(a,this.format)},updater:function(a,b){Ext.fly(a).down(this.getView().innerSelector,true).innerHTML=Ext.grid.column.Date.prototype.defaultRenderer.call(this,b)}},0,["datecolumn"],["component","box","container","headercontainer","gridcolumn","datecolumn"],{component:true,box:true,container:true,headercontainer:true,gridcolumn:true,datecolumn:true},["widget.datecolumn"],0,[Ext.grid.column,"Date",Ext.grid,"DateColumn"],0));(Ext.cmd.derive("Ext.grid.column.Number",Ext.grid.column.Column,{alternateClassName:"Ext.grid.NumberColumn",defaultFilterType:"number",format:"0,000.00",producesHTML:false,defaultRenderer:function(a){return Ext.util.Format.number(a,this.format)},updater:function(a,b){Ext.fly(a).down(this.getView().innerSelector,true).innerHTML=Ext.grid.column.Number.prototype.defaultRenderer.call(this,b)}},0,["numbercolumn"],["component","box","container","headercontainer","gridcolumn","numbercolumn"],{component:true,box:true,container:true,headercontainer:true,gridcolumn:true,numbercolumn:true},["widget.numbercolumn"],0,[Ext.grid.column,"Number",Ext.grid,"NumberColumn"],0));(Ext.cmd.derive("Ext.grid.column.RowNumberer",Ext.grid.column.Column,{alternateClassName:"Ext.grid.RowNumberer",isRowNumberer:true,text:" ",width:23,sortable:false,draggable:false,autoLock:true,lockable:false,align:"right",producesHTML:false,ignoreExport:true,constructor:function(a){var b=this;b.width=b.width;Ext.grid.column.Column.prototype.constructor.apply(this,arguments);b.sortable=false;b.scope=b},resizable:false,hideable:false,menuDisabled:true,dataIndex:"",cls:"x-row-numberer",tdCls:"x-grid-cell-row-numberer x-grid-cell-special",innerCls:"x-grid-cell-inner-row-numberer",rowspan:undefined,defaultRenderer:function(j,b,g,c,e,a,i){var d=this.rowspan,h=a.currentPage,k=i.store.indexOf(g);if(b&&d){b.tdAttr='rowspan="'+d+'"'}if(h>1){k+=(h-1)*a.pageSize}return k+1},updater:function(a,d,c,b,e){Ext.fly(a).down(this.getView().innerSelector,true).innerHTML=this.defaultRenderer(d,null,c,null,null,e,b)}},1,["rownumberer"],["component","box","container","headercontainer","gridcolumn","rownumberer"],{component:true,box:true,container:true,headercontainer:true,gridcolumn:true,rownumberer:true},["widget.rownumberer"],0,[Ext.grid.column,"RowNumberer",Ext.grid,"RowNumberer"],0));(Ext.cmd.derive("Ext.grid.column.Template",Ext.grid.column.Column,{alternateClassName:"Ext.grid.TemplateColumn",initComponent:function(){var a=this;a.tpl=(!Ext.isPrimitive(a.tpl)&&a.tpl.compile)?a.tpl:new Ext.XTemplate(a.tpl);a.hasCustomRenderer=true;Ext.grid.column.Column.prototype.initComponent.apply(this,arguments)},defaultRenderer:function(c,d,a){var b=Ext.apply({},a.data,a.getAssociatedData());return this.tpl.apply(b)},updater:function(a,b){Ext.fly(a).down(this.getView().innerSelector,true).innerHTML=Ext.grid.column.CheckColumn.prototype.defaultRenderer.call(this,b)}},0,["templatecolumn"],["component","box","container","headercontainer","gridcolumn","templatecolumn"],{component:true,box:true,container:true,headercontainer:true,gridcolumn:true,templatecolumn:true},["widget.templatecolumn"],0,[Ext.grid.column,"Template",Ext.grid,"TemplateColumn"],0));(Ext.cmd.derive("Ext.grid.column.Widget",Ext.grid.column.Column,{config:{defaultWidgetUI:{}},ignoreExport:true,sortable:false,onWidgetAttach:null,preventUpdate:true,stopSelection:true,initComponent:function(){var a=this,b;Ext.grid.column.Column.prototype.initComponent.apply(this,arguments);b=a.widget;a.widget=b=Ext.apply({},b);if(!b.ui){b.ui=a.getDefaultWidgetUI()[b.xtype]||"default"}a.isFixedSize=Ext.isNumber(b.width)},processEvent:function(g,i,j,a,h,d,b,k){var c;if(this.stopSelection&&g==="click"){c=d.getTarget(i.innerSelector);if(c&&c!==d.target){d.stopSelection=true}}},beforeRender:function(){var a=this,c=a.tdCls,b;a.listenerScopeFn=function(d){if(d==="this"){return this}return a.resolveListenerScope(d)};a.liveWidgets={};a.cachedStyles={};a.freeWidgetStack=[b=a.getFreeWidget()];c=c?c+" ":"";a.tdCls=c+b.getTdCls();a.setupViewListeners(a.getView());Ext.grid.column.Column.prototype.beforeRender.call(this)},afterRender:function(){var a=this.getView();Ext.grid.column.Column.prototype.afterRender.call(this);if(a&&a.viewReady&&!a.ownerGrid.reconfiguring){this.onViewRefresh(a,a.getViewRange())}},defaultRenderer:Ext.emptyFn,updater:function(a,c,b){this.updateWidget(b)},onResize:function(e){var d=this,b=d.liveWidgets,c=d.getView(),g,a;if(!d.isFixedSize&&d.rendered&&c&&c.viewReady){a=c.getEl().down(d.getCellInnerSelector());if(a){e-=parseInt(d.getCachedStyle(a,"padding-left"),10)+parseInt(d.getCachedStyle(a,"padding-right"),10);for(g in b){b[g].setWidth(e)}}}},onAdded:function(){var b=this,a;Ext.grid.column.Column.prototype.onAdded.apply(this,arguments);a=b.getView();if(a){b.setupViewListeners(a);if(a&&a.viewReady&&b.rendered&&a.getEl().down(b.getCellSelector())){b.onViewRefresh(a,a.getViewRange())}}},onRemoved:function(c){var d=this,a=d.liveWidgets,b=d.viewListeners,e;if(d.rendered){d.viewListeners=b&&Ext.destroy(b);if(!c){for(e in a){a[e].detachFromBody()}}}Ext.grid.column.Column.prototype.onRemoved.apply(this,arguments)},onDestroy:function(){var c=this,h=c.liveWidgets,g=c.freeWidgetStack,e,d,b,a;if(c.rendered){for(e in h){d=h[e];d.$widgetRecord=d.$widgetColumn=null;delete d.getWidgetRecord;delete d.getWidgetColumn;d.destroy()}for(b=0,a=g.length;b0){l=g.getModel();for(q=0;q-1},indexOfPlaceholder:function(a){return this.data.indexOf(a)},indexOfId:function(a){return this.data.indexOfKey(a)},indexOfTotal:function(a){return this.store.indexOf(a)},onAdd:function(a){var b=this;b.processStore(b.store);b.fireEvent("refresh",b);return false},onClear:function(b,a,d){var c=this;c.processStore(c.store);c.fireEvent("clear",c)},onIdChanged:function(a,d,c,b){this.data.updateKey(d,c)},onRefresh:function(){this.processStore(this.store);this.fireEvent("refresh",this)},onRemove:function(){var a=this;a.processStore(a.store);a.fireEvent("refresh",a);return false},onUpdate:function(k,g,c,e){var j=this,a=j.groupingFeature,l,d,i,b,h;if(k.isGrouped()){l=g.group=a.getGroup(g);if(l){d=a.getMetaGroup(g);if(e&&Ext.Array.contains(e,a.getGroupField())){return j.onRefresh(j.store)}if(d.isCollapsed){j.fireEvent("update",j,d.placeholder)}else{Ext.suspendLayouts();j.fireEvent("update",j,g,c,e);h=l.items;i=h[0];b=h[h.length-1];if(i!==g){i.group=l;j.fireEvent("update",j,i,"edit",e);delete i.group}if(b!==g&&b!==i&&a.showSummaryRow){b.group=l;j.fireEvent("update",j,b,"edit",e);delete b.group}Ext.resumeLayouts(true)}}delete g.group}else{j.fireEvent("update",j,g,c,e)}},onGroupChange:function(b,a){if(!a){this.processStore(b)}this.fireEvent("groupchange",b,a)},destroy:function(){var a=this;a.bindStore(null);Ext.destroyMembers(a,"data","groupingFeature");Ext.util.Observable.prototype.destroy.call(this)}},1,0,0,0,0,0,[Ext.grid.feature,"GroupStore"],0));(Ext.cmd.derive("Ext.grid.feature.Grouping",Ext.grid.feature.Feature,{eventPrefix:"group",eventSelector:".x-grid-group-hd",refreshData:{},wrapsItem:true,groupHeaderTpl:"{columnName}: {name}",depthToIndent:17,collapsedCls:"x-grid-group-collapsed",hdCollapsedCls:"x-grid-group-hd-collapsed",hdNotCollapsibleCls:"x-grid-group-hd-not-collapsible",collapsibleCls:"x-grid-group-hd-collapsible",ctCls:"x-group-hd-container",groupByText:"Group by this field",showGroupsText:"Show in groups",hideGroupedHeader:false,startCollapsed:false,enableGroupingMenu:true,enableNoGroups:true,collapsible:true,groupers:null,expandTip:"Click to expand. CTRL key collapses all others",collapseTip:"Click to collapse. CTRL/click collapses all others",showSummaryRow:false,outerTpl:["{%","if (!(this.groupingFeature.disabled || values.rows.length === 1 && values.rows[0].isSummary)) {","this.groupingFeature.setup(values.rows, values.view.rowValues);","}","this.nextTpl.applyOut(values, out, parent);","if (!(this.groupingFeature.disabled || values.rows.length === 1 && values.rows[0].isSummary)) {","this.groupingFeature.cleanup(values.rows, values.view.rowValues);","}","%}",{priority:200}],groupRowTpl:["{%","var me = this.groupingFeature,",'colspan = "colspan=" + values.columns.length;',"if (me.disabled || parent.rows.length === 1 && parent.rows[0].isSummary) {","values.needsWrap = false;","} else {","me.setupRowData(values.record, values.rowIndex, values);","}","%}",'','',"{% values.view.renderColumnSizer(values, out); %}",'','',"{%",'var groupTitleStyle = (!values.view.lockingPartner || (values.view.ownerCt === values.view.ownerCt.ownerLockable.lockedGrid) || (values.view.lockingPartner.headerCt.getVisibleGridColumns().length === 0)) ? "" : "visibility:hidden";',"%}",'
    ','
    ','{[values.groupHeaderTpl.apply(values.metaGroupCache, parent) || " "]}',"
    ","
    ","","","
    ",'',"{%","values.itemClasses.length = 0;","this.nextTpl.applyOut(values, out, parent);","%}","",'',"{%me.outputSummaryRecord(values.summaryRecord, values, out, parent);%}","","","{%this.nextTpl.applyOut(values, out, parent);%}","",{priority:200,beginRowSync:function(a){var b=this.groupingFeature;a.add("header",b.eventSelector);a.add("summary",b.summaryRowSelector)},syncContent:function(b,i,a){b=Ext.fly(b,"syncDest");i=Ext.fly(i,"syncSrc");var e=this.groupingFeature,d=b.down(e.eventSelector,true),c=i.down(e.eventSelector,true),h=b.down(e.summaryRowSelector,true),g=i.down(e.summaryRowSelector,true);if(d&&c){Ext.fly(d).syncContent(c)}if(h&&g){if(a){this.groupingFeature.view.updateColumns(h,g,a)}else{Ext.fly(h).syncContent(g)}}}}],init:function(c){var e=this,a=e.view,b=e.getGridStore(),d,g;a.isGrouping=b.isGrouped();e.mixins.summary.init.call(e);Ext.grid.feature.Feature.prototype.init.call(this,c);a.headerCt.on({columnhide:e.onColumnHideShow,columnshow:e.onColumnHideShow,columnmove:e.onColumnMove,scope:e});a.addTpl(Ext.XTemplate.getTpl(e,"outerTpl")).groupingFeature=e;a.addRowTpl(Ext.XTemplate.getTpl(e,"groupRowTpl")).groupingFeature=e;a.preserveScrollOnRefresh=true;if(b.isBufferedStore){e.collapsible=false}else{d=e.lockingPartner;if(d&&d.dataSource){e.dataSource=a.dataSource=g=d.dataSource}else{e.dataSource=a.dataSource=g=new Ext.grid.feature.GroupStore(e,b)}}c=c.ownerLockable||c;c.on("beforereconfigure",e.beforeReconfigure,e);a.on({afterrender:e.afterViewRender,scope:e,single:true});if(g){g.on("groupchange",e.onGroupChange,e)}else{e.setupStoreListeners(b)}e.mixins.summary.bindStore.call(e,c,c.getStore())},getGridStore:function(){return this.view.getStore()},indexOf:function(a){return this.dataSource.indexOf(a)},indexOfPlaceholder:function(a){return this.dataSource.indexOfPlaceholder(a)},isInCollapsedGroup:function(b){var e=this,d=e.getGridStore(),a=false,c;if(d.isGrouped()&&(c=e.getMetaGroup(b))){a=!!(c&&c.isCollapsed)}return a},createCache:function(){var a=this.metaGroupCache={},b=this.lockingPartner;if(b){b.metaGroupCache=a}a.map={};return a},getCache:function(){return this.metaGroupCache||this.createCache()},invalidateCache:function(){var a=this.lockingPartner;this.metaGroupCache=null;if(a){a.metaGroupCache=null}},vetoEvent:function(a,c,d,b){if(b.type!=="mouseover"&&b.type!=="mouseout"&&b.type!=="mouseenter"&&b.type!=="mouseleave"&&b.getTarget(this.eventSelector)){return false}},enable:function(){var c=this,a=c.view,b=c.getGridStore(),e=c.hideGroupedHeader&&c.getGroupedHeader(),d;a.isGrouping=true;if(a.lockingPartner){a.lockingPartner.isGrouping=true}Ext.grid.feature.Feature.prototype.enable.call(this);if(c.lastGrouper){b.group(c.lastGrouper);c.lastGrouper=null}if(e){e.hide()}d=c.view.headerCt.getMenu().down("#groupToggleMenuItem");if(d){d.setChecked(true,true)}},disable:function(){var c=this,a=c.view,b=c.getGridStore(),g=c.hideGroupedHeader&&c.getGroupedHeader(),e=b.getGrouper(),d;a.isGrouping=false;if(a.lockingPartner){a.lockingPartner.isGrouping=false}Ext.grid.feature.Feature.prototype.disable.call(this);if(e){c.lastGrouper=e;b.clearGrouping()}if(g){g.show()}d=c.view.headerCt.getMenu().down("#groupToggleMenuItem");if(d){d.setChecked(false,true);d.disable()}},afterViewRender:function(){var b=this,a=b.view;a.on({scope:b,groupclick:b.onGroupClick});if(b.enableGroupingMenu){b.injectGroupingMenu()}b.pruneGroupedHeader();b.lastGrouper=b.getGridStore().getGrouper();if(b.disabled){b.disable()}},injectGroupingMenu:function(){var a=this,b=a.view.headerCt;b.showMenuBy=a.showMenuBy;b.getMenuItems=a.getMenuItems()},onColumnHideShow:function(d,h){var l=this,m=l.view,b=m.headerCt,a=b.getMenu(),c=a.activeHeader,n=a.down("#groupMenuItem"),g,o=l.grid.getVisibleColumnManager().getColumns().length,k,j,e;if(c&&n){g=c.groupable===false||!c.dataIndex||l.view.headerCt.getVisibleGridColumns().length<2?"disable":"enable";n[g]()}if(m.rendered&&o){k=m.el.query("."+l.ctCls);for(e=0,j=k.length;e','','
    {rowBody}
    ',"","","{%","if(this.rowBody.bodyBefore) {","this.nextTpl.applyOut(values, out, parent);","}","%}",{priority:100,beginRowSync:function(a){a.add("rowBody",this.owner.eventSelector)},syncContent:function(c,g,b){var a=this.owner,d=Ext.fly(c).down(a.eventSelector,true),e;if(d&&(e=Ext.fly(g).down(a.eventSelector,true))){Ext.fly(d).syncContent(e)}}}],init:function(b){var c=this,a=c.view=b.getView();b.variableRowHeight=a.variableRowHeight=true;a.rowBodyFeature=c;b.mon(a,{element:"el",click:c.onClick,scope:c});a.headerCt.on({columnschanged:c.onColumnsChanged,scope:c});a.addTpl(c.outerTpl).rowBody=c;a.addRowTpl(Ext.XTemplate.getTpl(this,"extraRowTpl")).rowBody=c;Ext.grid.feature.Feature.prototype.init.apply(this,arguments)},onClick:function(c){var b=this,a=c.getTarget(b.eventSelector);if(a&&Ext.fly(a=(a.previousSibling||a.nextSibling)).is(b.view.rowSelector)){c.target=a;b.view.handleEvent(c)}},getSelectedRow:function(a,c){var b=a.getNode(c);if(b){return Ext.fly(b).down(this.eventSelector)}return null},onColumnsChanged:function(d){var b=this.view.el.query(this.rowBodyTdSelector),e=d.getVisibleGridColumns().length,a=b.length,c;for(c=0;c','','',"
    ",""],scrollable:{x:false,y:false},hidden:!d.showSummaryRow,itemId:"summaryBar",cls:[d.dockedSummaryCls,d.dockedSummaryCls+"-"+c],xtype:"component",dock:c,weight:10000000})[0]},afterrender:function(){b.getView().getScrollable().addPartner(d.summaryBar.getScrollable());d.onStoreUpdate()},single:true});b.headerCt.afterComponentLayout=Ext.Function.createSequence(b.headerCt.afterComponentLayout,function(){var e=this.getTableWidth(),g=d.summaryBar.innerCt;d.summaryBar.item.setWidth(e);if(this.tooNarrow){e+=Ext.getScrollbarSize().width}g.setWidth(e)})}else{if(b.bufferedRenderer){d.wrapsItem=true;a.addRowTpl(Ext.XTemplate.getTpl(d,"fullSummaryTpl")).summaryFeature=d;a.on("refresh",d.onViewRefresh,d)}else{d.wrapsItem=false;d.view.addFooterFn(d.renderSummaryRow)}}b.ownerGrid.on({beforereconfigure:d.onBeforeReconfigure,columnmove:d.onStoreUpdate,scope:d});d.bindStore(b,b.getStore())},onBeforeReconfigure:function(b,a){this.summaryRecord=null;if(a){this.bindStore(b,a)}},bindStore:function(b,a){var c=this;Ext.destroy(c.storeListeners);c.storeListeners=a.on({scope:c,destroyable:true,update:c.onStoreUpdate,datachanged:c.onStoreUpdate});Ext.grid.feature.AbstractSummary.prototype.bindStore.call(this,b,a)},renderSummaryRow:function(c,d,e){var b=c.view,g=b.findFeature("summary"),a,h;if(!g.disabled&&g.showSummaryRow){a=g.summaryRecord;d.push('');g.outputSummaryRecord((a&&a.isModel)?a:g.createSummaryRecord(b),c,d,e);d.push("
    ")}},toggleSummaryRow:function(d,a){var c=this,b=c.summaryBar;Ext.grid.feature.AbstractSummary.prototype.toggleSummaryRow.call(this,d,a);if(b){b.setVisible(c.showSummaryRow);c.onViewScroll()}},getSummaryBar:function(){return this.summaryBar},vetoEvent:function(a,c,d,b){return !b.getTarget(this.summaryRowSelector)},onViewScroll:function(){this.summaryBar.setScrollX(this.view.getScrollX())},onViewRefresh:function(b){var c=this,a,d;if(!c.disabled&&c.showSummaryRow&&!b.all.getCount()){a=c.createSummaryRecord(b);d=Ext.fly(b.getNodeContainer()).createChild({tag:"table",cellpadding:0,cellspacing:0,cls:c.summaryItemCls,style:"table-layout: fixed; width: 100%"},false,true);d.appendChild(Ext.fly(b.createRowElement(a,-1)).down(c.summaryRowSelector,true))}},createSummaryRecord:function(j){var h=this,d=j.headerCt.getGridColumns(),a=h.remoteRoot,g=h.summaryRecord,l=d.length,e,c,k,b,m;if(!g){m={id:j.id+"-summary-record"};g=h.summaryRecord=new Ext.data.Model(m)}g.beginEdit();if(a){b=h.generateSummaryData();if(b){g.set(b)}}else{for(e=0;e{text} {linkHrefCls}{childElCls}" href="{href}" target="{hrefTarget}" hidefocus="true" unselectable="on" tabindex="{tabIndex}" {$}="{.}">{text}
    ',maskOnDisable:false,iconAlign:"left",initComponent:function(){var b=this,a=b.cls?[b.cls]:[],c;if(b.hasOwnProperty("canActivate")){b.focusable=b.canActivate}if(b.plain){a.push("x-menu-item-plain")}if(a.length){b.cls=a.join(" ")}if(b.menu){c=b.menu;b.menu=null;b.setMenu(c)}Ext.Component.prototype.initComponent.apply(this,arguments)},canFocus:function(){var a=this;return a.focusable&&a.rendered&&a.canActivate!==false&&!a.destroying&&!a.destroyed&&a.isVisible(true)},onFocus:function(b){var a=this;Ext.Component.prototype.onFocus.call(this,b);if(!a.disabled){if(!a.plain){a.addCls(a.activeCls)}a.activated=true;if(a.hasListeners.activate){a.fireEvent("activate",a)}}},onFocusLeave:function(b){var a=this;Ext.Component.prototype.onFocusLeave.call(this,b);if(a.activated){if(!a.plain){a.removeCls(a.activeCls)}a.doHideMenu();a.activated=false;if(a.hasListeners.deactivate){a.fireEvent("deactivate",a)}}},doHideMenu:function(){var a=this.menu;this.cancelDeferExpand();if(a&&a.isVisible()){a.hide()}},deferHideParentMenus:function(){for(var a=this.getRefOwner();a&&((a.isMenu&&a.floating)||a.isMenuItem);a=a.getRefOwner()){if(a.isMenu){a.hide()}}},expandMenu:function(c,a){var b=this;if(b.activated&&b.menu){b.hideOnClick=false;b.cancelDeferHide();a=a==null?b.menuExpandDelay:a;if(a===0){b.doExpandMenu(c)}else{b.cancelDeferExpand();b.expandMenuTimer=Ext.defer(b.doExpandMenu,a,b,[c])}}},doExpandMenu:function(a){var b=this,c=b.menu;if(!c.isVisible()){b.parentMenu.activeChild=c;c.ownerCmp=b;c.parentMenu=b.parentMenu;c.constrainTo=document.body;c.autoFocus=!a||!a.pointerType;c.showBy(b,b.menuAlign)}},getRefItems:function(a){var c=this.menu,b;if(c){b=c.getRefItems(a);b.unshift(c)}return b||[]},getValue:function(){return this.value},hideMenu:function(a){var b=this;if(b.menu){b.cancelDeferExpand();b.hideMenuTimer=Ext.defer(b.doHideMenu,Ext.isNumber(a)?a:b.menuHideDelay,b)}},onClick:function(g){var d=this,c=d.clickHideDelay,h=g.browserEvent,b,a;if(!d.href||d.disabled){g.stopEvent();if(d.disabled){return false}}if(d.disabled||d.handlingClick){return}if(d.hideOnClick){if(!c){d.deferHideParentMenus()}else{d.deferHideParentMenusTimer=Ext.defer(d.deferHideParentMenus,c,d)}}b=d.fireEvent("click",d,g);if(d.destroyed){return}if(b!==false&&d.handler){Ext.callback(d.handler,d.scope,[d,g],0,d)}if(Ext.isIE9m){a=h.returnValue===false?true:false}else{a=!!h.defaultPrevented}if(d.href&&g.type!=="click"&&!a){d.handlingClick=true;d.itemEl.dom.click();d.handlingClick=false}if(!d.hideOnClick){d.focus()}return b},onRemoved:function(){var a=this;if(a.activated&&a.parentMenu.activeItem===a){a.parentMenu.deactivateActiveItem()}Ext.Component.prototype.onRemoved.apply(this,arguments);a.parentMenu=a.ownerCmp=null},beforeDestroy:function(){var a=this;if(a.rendered){a.clearTip()}Ext.Component.prototype.beforeDestroy.call(this)},onDestroy:function(){var a=this;a.cancelDeferExpand();a.cancelDeferHide();clearTimeout(a.deferHideParentMenusTimer);a.setMenu(null);Ext.Component.prototype.onDestroy.apply(this,arguments)},beforeRender:function(){var j=this,k=j.glyph,i=Ext._glyphFontFamily,e=!!(j.icon||j.iconCls||k),l=!!j.menu,g=((j.iconAlign==="right")&&!l),c=j.isMenuCheckItem,a=[],d=j.ownerCt,h=d.plain,b;if(j.plain){j.ariaEl="el"}Ext.Component.prototype.beforeRender.call(this);if(e){if(l&&j.showCheckbox){e=false}}if(typeof k==="string"){b=k.split("@");k=b[0];i=b[1]}if(!h||(e&&!g)||c){if(d.showSeparator&&!h){a.push(j.indentCls)}else{a.push(j.indentNoSeparatorCls)}}if(l){a.push(j.indentRightArrowCls)}else{if(e&&(g||c)){a.push(j.indentRightIconCls)}}Ext.applyIf(j.renderData,{hasHref:!!j.href,href:j.href||"#",hrefTarget:j.hrefTarget,icon:j.icon,iconCls:j.iconCls,glyph:k,glyphCls:k?"x-menu-item-glyph":undefined,glyphFontFamily:i,hasIcon:e,hasMenu:l,indent:!h||e||c,isCheckItem:c,rightIcon:g,plain:j.plain,text:j.text,arrowCls:j.arrowCls,baseIconCls:j.baseIconCls,textCls:j.textCls,indentCls:a.join(" "),linkCls:j.linkCls,linkHrefCls:j.linkHrefCls,groupCls:j.group?j.groupCls:"",tabIndex:j.tabIndex})},onRender:function(){var a=this;Ext.Component.prototype.onRender.apply(this,arguments);if(a.tooltip){a.setTooltip(a.tooltip,true)}},getMenu:function(){return this.menu||null},setMenu:function(i,h){var g=this,c=g.menu,b=g.arrowEl,a=g.ariaEl.dom,e,d;if(c){c.ownerCmp=c.parentMenu=null;if(h===true||(h!==false&&g.destroyMenu)){Ext.destroy(c)}if(a){a.removeAttribute("aria-haspopup");a.removeAttribute("aria-owns")}else{e=(g.ariaRenderAttributes||(g.ariaRenderAttributes={}));delete e["aria-haspopup"];delete e["aria-owns"]}}if(i){d=i.isMenu;i=g.menu=Ext.menu.Manager.get(i,{ownerCmp:g,focusOnToFront:false});i.setOwnerCmp(g,d);if(a){a.setAttribute("aria-haspopup",true);a.setAttribute("aria-owns",i.id)}else{e=(g.ariaRenderAttributes||(g.ariaRenderAttributes={}));e["aria-haspopup"]=true;e["aria-owns"]=i.id}}else{i=g.menu=null}if(i&&g.rendered&&!g.destroying&&b){b[i?"addCls":"removeCls"](g.arrowCls)}},setHandler:function(b,a){this.handler=b||null;this.scope=a},setIcon:function(b){var a=this.iconEl,c=this.icon;if(a){a.src=b||Ext.BLANK_IMAGE_URL}this.icon=b;this.fireEvent("iconchange",this,c,b)},setIconCls:function(b){var d=this,a=d.iconEl,c=d.iconCls;if(a){if(d.iconCls){a.removeCls(d.iconCls)}if(b){a.addCls(b)}}d.iconCls=b;d.fireEvent("iconchange",d,c,b)},setText:function(d){var c=this,b=c.textEl||c.el,a=c.text;c.text=d;if(c.rendered){b.setHtml(d||"");c.updateLayout()}c.fireEvent("textchange",c,a,d)},getTipAttr:function(){return this.tooltipType==="qtip"?"data-qtip":"title"},clearTip:function(){if(Ext.quickTipsActive&&Ext.isObject(this.tooltip)){Ext.tip.QuickTipManager.unregister(this.itemEl)}},setTooltip:function(c,a){var b=this;if(b.rendered){if(!a){b.clearTip()}if(Ext.quickTipsActive&&Ext.isObject(c)){Ext.tip.QuickTipManager.register(Ext.apply({target:b.itemEl.id},c));b.tooltip=c}else{b.itemEl.dom.setAttribute(b.getTipAttr(),c)}}else{b.tooltip=c}return b},privates:{cancelDeferExpand:function(){window.clearTimeout(this.expandMenuTimer)},cancelDeferHide:function(){window.clearTimeout(this.hideMenuTimer)},getFocusEl:function(){return this.plain?this.el:this.itemEl}}},0,["menuitem"],["component","box","menuitem"],{component:true,box:true,menuitem:true},["widget.menuitem"],[[Ext.mixin.Queryable.prototype.mixinId||Ext.mixin.Queryable.$className,Ext.mixin.Queryable]],[Ext.menu,"Item",Ext.menu,"TextItem"],0));(Ext.cmd.derive("Ext.menu.CheckItem",Ext.menu.Item,{checkedCls:"x-menu-item-checked",uncheckedCls:"x-menu-item-unchecked",groupCls:"x-menu-group-icon",hideOnClick:false,checkChangeDisabled:false,submenuText:"{0} submenu",ariaRole:"menuitemcheckbox",childEls:["checkEl"],showCheckbox:true,isMenuCheckItem:true,checkboxCls:"x-menu-item-checkbox",initComponent:function(){var a=this;a.checked=!!a.checked;Ext.menu.Item.prototype.initComponent.apply(this,arguments);if(a.group){Ext.menu.Manager.registerCheckable(a);if(a.initialConfig.hideOnClick!==false){a.hideOnClick=true}}},beforeRender:function(){var b=this,a;Ext.menu.Item.prototype.beforeRender.call(this);Ext.apply(b.renderData,{checkboxCls:b.checkboxCls,showCheckbox:b.showCheckbox});a=(b.ariaRenderAttributes||(b.ariaRenderAttributes={}));a["aria-checked"]=b.menu?"mixed":b.checked;if(b.menu){a["aria-label"]=Ext.String.formatEncode(b.submenuText,b.text)}},afterRender:function(){var a=this;Ext.menu.Item.prototype.afterRender.call(this);a.checked=!a.checked;a.setChecked(!a.checked,true);if(a.checkChangeDisabled){a.disableCheckChange()}if(Ext.isGecko&&a.checkEl){a.checkEl.on("mousedown",a.onMouseDownCheck)}},disableCheckChange:function(){var b=this,a=b.checkEl;if(a){a.addCls(b.disabledCls)}if(Ext.isIE8&&b.rendered){b.el.repaint()}b.checkChangeDisabled=true},enableCheckChange:function(){var b=this,a=b.checkEl;if(a){a.removeCls(b.disabledCls)}b.checkChangeDisabled=false},onMouseDownCheck:function(a){a.preventDefault()},onClick:function(b){var a=this;if(!(a.disabled||a.checkChangeDisabled||a.checked&&a.group||a.menu&&"touch"===b.pointerType&&!a.checkEl.contains(b.target))){a.setChecked(!a.checked);if(b.type==="keydown"&&a.menu){return false}}Ext.menu.Item.prototype.onClick.call(this,b)},onDestroy:function(){Ext.menu.Manager.unregisterCheckable(this);Ext.menu.Item.prototype.onDestroy.apply(this,arguments)},setText:function(c){var b=this,a=b.ariaEl.dom;Ext.menu.Item.prototype.setText.call(this,c);if(a&&b.menu){a.setAttribute("aria-label",Ext.String.formatEncode(b.submenuText,c))}},setChecked:function(e,c){var d=this,g=d.checkedCls,h=d.uncheckedCls,b=d.el,a=d.ariaEl.dom;if(d.checked!==e&&(c||d.fireEvent("beforecheckchange",d,e)!==false)){if(b){if(e){b.addCls(g);b.removeCls(h)}else{b.addCls(h);b.removeCls(g)}}if(a){a.setAttribute("aria-checked",d.menu?"mixed":!!e)}d.checked=e;Ext.menu.Manager.onCheckChange(d,e);if(!c){Ext.callback(d.checkHandler,d.scope,[d,e],0,d);d.fireEvent("checkchange",d,e)}}}},0,["menucheckitem"],["component","box","menuitem","menucheckitem"],{component:true,box:true,menuitem:true,menucheckitem:true},["widget.menucheckitem"],0,[Ext.menu,"CheckItem"],0));(Ext.cmd.derive("Ext.menu.Separator",Ext.menu.Item,{focusable:false,canActivate:false,hideOnClick:false,plain:true,separatorCls:"x-menu-item-separator",text:" ",ariaRole:"separator",beforeRender:function(){this.addCls(this.separatorCls);Ext.menu.Item.prototype.beforeRender.call(this)}},0,["menuseparator"],["component","box","menuitem","menuseparator"],{component:true,box:true,menuitem:true,menuseparator:true},["widget.menuseparator"],0,[Ext.menu,"Separator"],0));(Ext.cmd.derive("Ext.menu.Menu",Ext.panel.Panel,{enableKeyNav:true,allowOtherMenus:false,ariaRole:"menu",floating:true,constrain:true,hidden:true,hideMode:"visibility",ignoreParentClicks:false,isMenu:true,showSeparator:true,minWidth:undefined,defaultMinWidth:120,defaultAlign:"tl-bl?",focusOnToFront:false,bringParentToFront:false,defaultFocus:":focusable",menuClickBuffer:0,baseCls:"x-menu",_iconSeparatorCls:"x-menu-icon-separator",_itemCmpCls:"x-menu-item-cmp",layout:{type:"vbox",align:"stretchmax",overflowHandler:"Scroller"},initComponent:function(){var c=this,a=["x-menu"],d=c.bodyCls?[c.bodyCls]:[],e=c.floating!==false,b={element:"el",click:c.onClick,mouseover:c.onMouseOver,scope:c};if(Ext.supports.Touch){b.pointerdown=c.onMouseOver}c.on(b);c.on({beforeshow:c.onBeforeShow,scope:c});if(c.plain){a.push("x-menu-plain")}c.cls=a.join(" ");d.push("x-menu-body",Ext.dom.Element.unselectableCls);c.bodyCls=d.join(" ");if(e){if(c.minWidth===undefined){c.minWidth=c.defaultMinWidth}}else{c.hidden=!!c.initialConfig.hidden;c.constrain=false}Ext.panel.Panel.prototype.initComponent.apply(this,arguments);Ext.override(c.getLayout(),{configureItem:c.configureItem})},initFloatConstrain:Ext.emptyFn,getInherited:function(){var a=Ext.panel.Panel.prototype.getInherited.call(this);a.hidden=this.hidden;return a},beforeRender:function(){var a=this;Ext.panel.Panel.prototype.beforeRender.apply(this,arguments);if(!a.getSizeModel().width.shrinkWrap){a.layout.align="stretch"}if(a.floating){a.ariaRenderAttributes=a.ariaRenderAttributes||{};a.ariaRenderAttributes["aria-expanded"]=!!a.autoShow}},onBoxReady:function(){var b=this,c=b._iconSeparatorCls,a=b.focusableKeyNav;if(a){a.map.processEventScope=b;a.map.processEvent=function(d){if(d.keyCode===d.ESC){d.target=this.el.dom}return d};a.map.addBinding([{key:Ext.event.Event.ESC,handler:b.onEscapeKey,scope:b},{key:/[\w]/,handler:b.onShortcutKey,scope:b,shift:false,ctrl:false,alt:false}])}else{b.escapeKeyNav=new Ext.util.KeyNav(b.el,{eventName:"keydown",scope:b,esc:b.onEscapeKey})}Ext.panel.Panel.prototype.onBoxReady.apply(this,arguments);if(b.showSeparator){b.iconSepEl=b.body.insertFirst({role:"presentation",cls:c+" "+c+"-"+b.ui,html:" "})}if(Ext.supports.MSPointerEvents||Ext.supports.PointerEvents){b.el.on({scope:b,click:b.preventClick,translate:false})}b.mouseMonitor=b.el.monitorMouseLeave(100,b.onMouseLeave,b)},onFocusLeave:function(b){var a=this;Ext.panel.Panel.prototype.onFocusLeave.call(this,b);a.mixins.focusablecontainer.onFocusLeave.call(a,b);if(a.floating){a.hide()}},canActivateItem:function(a){return a&&a.isFocusable()},deactivateActiveItem:function(){var a=this,b=a.lastFocusedChild;if(b){b.blur()}},getItemFromEvent:function(d){var a=this,c=a.layout.getRenderTarget().dom,b=d.getTarget();while(b.parentNode!==c){b=b.parentNode;if(!b){return}}return Ext.getCmp(b.id)},lookupComponent:function(b){var a=this;if(typeof b==="string"){b=a.lookupItemFromString(b)}else{if(Ext.isObject(b)){b=a.lookupItemFromObject(b)}}if(!b.dock){b.minWidth=b.minWidth||a.minWidth}return b},lookupItemFromObject:function(b){var a=this;if(!b.isComponent){if(!b.xtype){b=Ext.create("Ext.menu."+(Ext.isBoolean(b.checked)?"Check":"")+"Item",b)}else{b=Ext.ComponentManager.create(b,b.xtype)}}if(b.isMenuItem){b.parentMenu=a}return b},lookupItemFromString:function(a){return(a==="separator"||a==="-")?new Ext.menu.Separator():new Ext.menu.Item({canActivate:false,hideOnClick:false,plain:true,text:a})},configureItem:function(c){var b=this.owner,e="x-",d=b.ui,a,g;if(c.isMenuItem){c.setUI(d)}else{if(b.items.getCount()>1&&!c.rendered&&!c.dock){g=b._itemCmpCls;a=[g+" "+g+"-"+d];if(!b.plain&&(c.indent!==false||c.iconCls==="no-icon")){a.push(e+"menu-item-indent-"+d)}if(c.rendered){c.el.addCls(a)}else{c.cls=(c.cls||"")+" "+a.join(" ")}c.$extraMenuCls=a}}this.callParent(arguments)},onRemove:function(a){Ext.panel.Panel.prototype.onRemove.call(this,a);if(!a.destroyed&&a.$extraMenuCls){a.el.removeCls(a.$extraMenuCls)}},onClick:function(h){var g=this,c=h.type,d,b,a=c==="keydown";if(g.disabled){h.stopEvent();return}d=g.getItemFromEvent(h);if(d&&d.isMenuItem){if(!d.menu||!g.ignoreParentClicks){b=d.onClick(h)}else{h.stopEvent()}if(d.menu&&b!==false&&a){d.expandMenu(h,0)}}if(!d||d.disabled){d=undefined}g.fireEvent("click",g,d,h)},onDestroy:function(){var a=this;if(a.escapeKeyNav){a.escapeKeyNav.destroy()}a.parentMenu=a.ownerCmp=a.escapeKeyNav=null;if(a.rendered){a.el.un(a.mouseMonitor);Ext.destroy(a.iconSepEl)}Ext.menu.Manager.onHide(a);Ext.panel.Panel.prototype.onDestroy.apply(this,arguments)},onMouseLeave:function(a){if(this.disabled){return}this.fireEvent("mouseleave",this,a)},onMouseOver:function(h){var g=this,i=h.getRelatedTarget(),b=!g.el.contains(i),d=g.getItemFromEvent(h),c=g.parentMenu,a=g.ownerCmp;if(b&&c){c.setActiveItem(a);a.cancelDeferHide();c.mouseMonitor.mouseenter()}if(g.disabled){return}if(d){if(!d.containsFocus){d.focus()}if(d.expandMenu){d.expandMenu(h)}}if(b){g.fireEvent("mouseenter",g,h)}g.fireEvent("mouseover",g,d,h)},setActiveItem:function(b){var a=this;if(b&&(b!==a.lastFocusedChild)){a.focusChild(b,1)}},onEscapeKey:function(){if(this.floating){this.hide()}},onShortcutKey:function(j,h){var b=String.fromCharCode(h.getCharCode()),c=this.query(">[text]"),a=c.length,g=this.lastFocusedChild,k=Ext.Array.indexOf(c,g),d=k;for(;;){if(++d===a){d=0}g=c[d];if(d===k){return}if(g.text&&g.text[0].toUpperCase()===b){g.focus();return}}},onFocusableContainerTabKey:function(a){if(this.floating){this.hide()}},onFocusableContainerEnterKey:function(a){this.onClick(a)},onFocusableContainerSpaceKey:function(a){this.onClick(a)},onFocusableContainerLeftKey:function(a){a.preventDefault();if(this.parentMenu){this.ownerCmp.focus();this.hide()}},onFocusableContainerRightKey:function(b){var a=this,c=a.lastFocusedChild;b.preventDefault();if(c&&c.expandMenu){c.expandMenu(b,0)}},onBeforeShow:function(){if(Ext.Date.getElapsed(this.lastHide)+c)){a.up("menuitem").setChecked(false,true);if(b.gt.getValue()!=null){j.gt=null}}}}j[i.filterKey]=c;h.setValue(j);g.up("menu").hide()}},0,0,0,0,["grid.filter.date"],0,[Ext.grid.filters.filter,"Date"],0));(Ext.cmd.derive("Ext.grid.filters.filter.List",Ext.grid.filters.filter.SingleFilter,{type:"list",operator:"in",itemDefaults:{checked:false,hideOnClick:false},idField:"id",labelField:"text",labelIndex:null,loadingText:"Loading...",loadOnShow:true,single:false,plain:true,constructor:function(a){var c=this,b;Ext.grid.filters.filter.SingleFilter.prototype.constructor.call(this,a);c.labelIndex=c.labelIndex||c.column.dataIndex;if(!c.options&&(c.value!=null)&&c.active){b=c.getGridStore();if(!b.isEmptyStore){b.on(c.getGridStoreListeners())}c.grid.on("reconfigure",c.onReconfigure,c);c.inferOptionsFromGridStore=true}},destroy:function(){var c=this,a=c.store,b=c.autoStore,d=c.gridStoreListeners;if(a){if(b||a.autoDestroy){a.destroy()}else{a.un("load",c.bindMenuStore,c)}c.store=null}if(c.inferOptionsFromGridStore){c.grid.un("reconfigure",c.onReconfigure,c)}if(d){c.getGridStore().un(d);c.gridStoreListeners=null}Ext.grid.filters.filter.SingleFilter.prototype.destroy.call(this)},activateMenu:function(){var e=this,g=e.filter.getValue(),c,d,a,b;if(!g||!g.length){return}c=e.menu.items;for(d=0,a=c.length;d-1){b.setChecked(true,true)}}},bindMenuStore:function(a){var b=this;if(b.grid.destroyed||b.preventFilterRemoval){return}b.createListStore(a);b.createMenuItems(b.store);b.loaded=true},createListStore:function(n){var h=this,m=h.store,j=n.isStore,b=h.idField,d=h.labelField,k=false,a,c,e,g,l;if(j){if(n!==h.getGridStore()){k=true;m=h.store=n}else{h.autoStore=true;a=h.getOptionsFromStore(n)}}else{a=[];for(e=0,g=n.length;etablepanel:not(hidden)>tableview");if(a){a.focus()}},focusRow:function(c){var a,b=this.getNavigationModel().lastFocused;a=b?b.view:this.normalView;a.focusRow(c)},focusCell:function(a){a.view.focusCell(a)},onRowFocus:function(){this.relayFn("onRowFocus",arguments)},isVisible:function(a){return this.ownerGrid.isVisible(a)},getFocusEl:function(){var a,b=this.getNavigationModel().lastFocused;a=b?b.view:this.normalView;return a.getFocusEl()},getCellInclusive:function(d,b){var a=d.column,c=this.lockedGrid.getColumnManager().getColumns().length;if(a>=c){d=Ext.apply({},d);d.column-=c;return this.normalView.getCellInclusive(d,b)}else{return this.lockedView.getCellInclusive(d,b)}},getHeaderByCell:function(a){if(a){return this.getVisibleColumnManager().getHeaderById(a.getAttribute("data-columnId"))}return false},onRowSelect:function(){this.relayFn("onRowSelect",arguments)},onRowDeselect:function(){this.relayFn("onRowDeselect",arguments)},onCellSelect:function(a){a.column.getView().onCellSelect({record:a.record,column:a.column})},onCellDeselect:function(a){a.column.getView().onCellDeselect({record:a.record,column:a.column})},getCellByPosition:function(e,c){var d=this,a=e.view,b=e.column;if(a===d){e=new Ext.grid.CellContext(b.getView()).setPosition(e.record,e.column)}return a.getCellByPosition(e,c)},getRecord:function(b){var a=this.lockedView.getRecord(b);if(!a){a=this.normalView.getRecord(b)}return a},scrollBy:function(){var a=this.normalView;a.scrollBy.apply(a,arguments)},ensureVisible:function(){var a=this.normalView;a.ensureVisible.apply(a,arguments)},disable:function(){this.relayFn("disable",arguments)},enable:function(){this.relayFn("enable",arguments)},addElListener:function(){this.relayFn("addElListener",arguments)},refreshNode:function(){this.relayFn("refreshNode",arguments)},addRowCls:function(){this.relayFn("addRowCls",arguments)},removeRowCls:function(){this.relayFn("removeRowCls",arguments)},destroy:function(){var a=this;a.rendered=false;a.bindStore(null,false,"dataSource");Ext.destroy(a.lockedViewEventRelayers,a.normalViewEventRelayers);a.lockedViewEventRelayers=a.normalViewEventRelayers=null;a.callParent();Ext.destroy(a.loadMask,a.navigationModel,a.selModel);a.lockedView.lockingPartner=a.normalView.lockingPartner=null;a.lockedGrid=a.lockedView=a.normalGrid=a.normalView=null;a.loadMask=a.navigationModel=a.selModel=a.headerCt=null;a.ownerGrid=a.storeListeners=null}},1,0,0,0,0,[[Ext.util.Observable.prototype.mixinId||Ext.util.Observable.$className,Ext.util.Observable],[Ext.util.StoreHolder.prototype.mixinId||Ext.util.StoreHolder.$className,Ext.util.StoreHolder],[Ext.util.Focusable.prototype.mixinId||Ext.util.Focusable.$className,Ext.util.Focusable]],[Ext.grid.locking,"View",Ext.grid,"LockingView"],function(){this.borrow(Ext.Component,["up"]);this.borrow(Ext.view.AbstractView,["doFirstRefresh","applyFirstRefresh"]);this.borrow(Ext.view.Table,["cellSelector","selectedCellCls","selectedItemCls"])}));(Ext.cmd.derive("Ext.grid.locking.Lockable",Ext.Base,{alternateClassName:"Ext.grid.Lockable",syncRowHeight:true,headerCounter:0,scrollDelta:40,lockedGridCls:"x-grid-inner-locked",normalGridCls:"x-grid-inner-normal",lockingBodyCls:"x-grid-locking-body",unlockText:"Unlock",lockText:"Lock",bothCfgCopy:["hideHeaders","enableColumnHide","enableColumnMove","enableColumnResize","sortableColumns","multiColumnSort","columnLines","rowLines","variableRowHeight","numFromEdge","trailingBufferZone","leadingBufferZone","scrollToLoadBuffer","syncRowHeight"],normalCfgCopy:["scroll"],lockedCfgCopy:[],determineXTypeToCreate:function(e){var c=this,h,d,b,g,a;if(c.subGridXType){h=c.subGridXType}else{if(!e){return"gridpanel"}d=this.getXTypes().split("/");b=d.length;g=d[b-1];a=d[b-2];if(a!=="tablepanel"){h=a}else{h=g}}return h},injectLockable:function(){this.focusable=false;this.lockable=true;this.hasView=true;var w=this,l=Ext.getScrollbarSize(),p=l.width,e=w.store=Ext.StoreManager.lookup(w.store),c=w.lockedViewConfig,v=w.normalViewConfig,n=Ext.Object,j,k,t,h,m,b,g,s,u=w.viewConfig,a=u&&u.loadMask,o=(a!==undefined)?a:w.loadMask,q=w.bufferedRenderer,r=p>0&&Ext.supports.touchScroll!==2,d=w.getInherited().rtl;j=w.constructLockableFeatures();w.features=null;k=w.constructLockablePlugins();w.plugins=k.topPlugins;t={id:w.id+"-locked",$initParent:w,isLocked:true,bufferedRenderer:q,ownerGrid:w,ownerLockable:w,xtype:w.determineXTypeToCreate(true),store:e,reserveScrollbar:r,scrollable:{indicators:{x:true,y:false}},scrollerOwner:false,animate:false,border:false,cls:w.lockedGridCls,isLayoutRoot:function(){return this.floatedFromCollapse||w.normalGrid.floatedFromCollapse},features:j.lockedFeatures,plugins:k.lockedPlugins};h={id:w.id+"-normal",$initParent:w,isLocked:false,bufferedRenderer:q,ownerGrid:w,ownerLockable:w,xtype:w.determineXTypeToCreate(),store:e,reserveScrollbar:w.reserveScrollbar,scrollerOwner:false,border:false,cls:w.normalGridCls,isLayoutRoot:function(){return this.floatedFromCollapse||w.lockedGrid.floatedFromCollapse},features:j.normalFeatures,plugins:k.normalPlugins};w.addCls("x-grid-locked");Ext.copy(h,w,w.bothCfgCopy,true);Ext.copy(t,w,w.bothCfgCopy,true);Ext.copy(h,w,w.normalCfgCopy,true);Ext.copy(t,w,w.lockedCfgCopy,true);Ext.apply(h,w.normalGridConfig);Ext.apply(t,w.lockedGridConfig);for(m=0;m>#normalHeaderCt",items:l},j={itemId:"normalHeaderCt",stretchMaxPartner:"^^>>#lockedHeaderCt",items:c},o={locked:b,normal:j},a;if(Ext.isObject(g)){Ext.applyIf(b,g);Ext.applyIf(j,g);a=Ext.apply({},g);delete a.items;Ext.apply(m,a);g=g.items}m.constructing=true;for(h=0,k=g.length;h0&&b)){j.stopEvent();i+=a;c.setScrollY(i);d.normalGrid.getView().setScrollY(i);d.onNormalViewScroll()}}},onLockedViewScroll:function(){var g=this,e=g.lockedGrid.getView(),d=g.normalGrid.getView(),c=e.getScrollY(),h=d.getScrollY(),a,b;if(h!==c){d.setScrollY(c);if(d.bufferedRenderer){b=e.body.dom;a=d.body.dom;a.style.position="absolute";a.style.top=b.style.top}}},onNormalViewScroll:function(){var d=this,c=d.lockedGrid.getView(),b=d.normalGrid.getView(),a=c.getScrollY(),g=b.getScrollY(),e;if(g!==a){c.setScrollY(g);if(b.bufferedRenderer){e=c.body;if(e.dom){e.dom.style.position="absolute";e.translate(null,b.bufferedRenderer.bodyTop)}}}},syncRowHeights:function(){if(!this.destroyed){var d=this,b=d.normalGrid.getView(),c=d.lockedGrid.getView(),g=b.syncRowHeightBegin(),a=c.syncRowHeightBegin(),e;b.syncRowHeightMeasure(g);c.syncRowHeightMeasure(a);b.syncRowHeightFinish(g,a);c.syncRowHeightFinish(a,g);e=b.getScrollY();c.setScrollY(e)}},modifyHeaderCt:function(){var a=this;a.lockedGrid.headerCt.getMenuItems=a.getMenuItems(a.lockedGrid.headerCt.getMenuItems,true);a.normalGrid.headerCt.getMenuItems=a.getMenuItems(a.normalGrid.headerCt.getMenuItems,false);a.lockedGrid.headerCt.showMenuBy=Ext.Function.createInterceptor(a.lockedGrid.headerCt.showMenuBy,a.showMenuBy);a.normalGrid.headerCt.showMenuBy=Ext.Function.createInterceptor(a.normalGrid.headerCt.showMenuBy,a.showMenuBy)},onUnlockMenuClick:function(){this.unlock()},onLockMenuClick:function(){this.lock()},showMenuBy:function(a,c,h){var g=this.getMenu(),d=g.down("#unlockItem"),e=g.down("#lockItem"),b=d.prev();if(h.lockable===false){b.hide();d.hide();e.hide()}else{b.show();d.show();e.show();if(!d.initialConfig.disabled){d.setDisabled(h.lockable===false)}if(!e.initialConfig.disabled){e.setDisabled(!h.isLockable())}}},getMenuItems:function(g,c){var h=this,i=h.unlockText,a=h.lockText,j="x-hmenu-unlock",b="x-hmenu-lock",e=h.onUnlockMenuClick.bind(h),d=h.onLockMenuClick.bind(h);return function(){var k=g.call(this);k.push("-",{itemId:"unlockItem",iconCls:j,text:i,handler:e,disabled:!c});k.push({itemId:"lockItem",iconCls:b,text:a,handler:d,disabled:c});return k}},delaySyncLockedWidth:function(){var b=this,a=b.syncLockedWidthTask;if(!b.view.all.getCount()){return}if(!a){a=b.syncLockedWidthTask=new Ext.util.DelayedTask(b.syncLockedWidth,b)}a.delay(1)},syncLockedWidth:function(){var e=this,i=e.rendered,c=e.lockedGrid,d=c.view,h=e.normalGrid,g=c.getVisibleColumnManager().getColumns().length,a=h.getVisibleColumnManager().getColumns().length,b=e.syncLockedWidthTask;if(b){b.cancel()}Ext.suspendLayouts();if(a){h.show();if(g){if(i&&c.shrinkWrapColumns&&!c.headerCt.forceFit){delete c.flex;c.setWidth(c.headerCt.getTableWidth()+c.gridPanelBorderWidth)}c.addCls(e.lockedGridCls);c.show();if(c.split){e.child("splitter").show();e.addCls("x-grid-locked-split")}}else{if(i){c.getView().clearViewEl(true)}c.hide();if(c.split){e.child("splitter").hide();e.removeCls("x-grid-locked-split")}}if(Ext.supports.touchScroll!==2&&Ext.Component.pendingLayouts){d.getScrollable().setX(true)}if(i){e.ignoreMousewheel=d.scrollFlags.y}}else{h.hide();c.flex=1;delete c.width;c.removeCls(e.lockedGridCls);c.show();e.ignoreMousewheel=true}Ext.resumeLayouts(true);return[g,a]},onLockedHeaderSortChange:Ext.emptyFn,onNormalHeaderSortChange:Ext.emptyFn,lock:function(g,l,e){var i=this,h=i.normalGrid,c=i.lockedGrid,d=h.view,n=c.view,k=h.headerCt,j,a,b,m;g=g||k.getMenu().activeHeader;b=g.hasFocus;e=e||c.headerCt;a=g.ownerCt;if(!g.isLockable()){return}if(g.flex&&c.shrinkWrapColumns){g.width=g.getWidth();g.flex=null}Ext.suspendLayouts();if(c.hidden){if(!c.componentLayoutCounter){if(n.bufferedRenderer){n.bufferedRenderer.onViewResize(n,0,d.getHeight())}m=d.getScrollY()}c.show()}d.blockRefresh=n.blockRefresh=true;g.ownerCmp=g.ownerCt;a.remove(g,false);g.locked=true;if(Ext.isDefined(l)){e.insert(l,g)}else{e.add(g)}d.blockRefresh=n.blockRefresh=false;g.ownerCmp=null;j=i.syncLockedWidth();if(j[0]){c.getView().refreshView()}if(j[1]){h.getView().refreshView()}i.fireEvent("lockcolumn",i,g);Ext.resumeLayouts(true);if(m){n.setScrollY(m);d.setScrollY(m)}if(b){g.focus()}},unlock:function(e,k,d){var h=this,g=h.normalGrid,b=h.lockedGrid,c=g.view,l=b.view,j=b.headerCt,i,a;if(!Ext.isDefined(k)){k=0}e=e||j.getMenu().activeHeader;a=e.hasFocus;d=d||g.headerCt;Ext.suspendLayouts();c.blockRefresh=l.blockRefresh=true;e.ownerCmp=e.ownerCt;e.ownerCt.remove(e,false);e.locked=false;d.insert(k,e);c.blockRefresh=l.blockRefresh=false;e.ownerCmp=null;i=h.syncLockedWidth();if(i[0]){b.getView().refreshView()}if(i[1]){g.getView().refreshView()}h.fireEvent("unlockcolumn",h,e);Ext.resumeLayouts(true);if(a){e.focus()}},reconfigureLockable:function(b,c){var e=this,i=e.store,h=e.lockedGrid,g=e.normalGrid,a,d;if(b&&b!==i){b=Ext.data.StoreManager.lookup(b);e.store=b;h.view.blockRefresh=g.view.blockRefresh=true;h.bindStore(b);a=h.view;a.store=b;if(!a.dataSource.isFeatureStore){a.dataSource=b}if(a.bufferedRenderer){a.bufferedRenderer.bindStore(b)}g.bindStore(b);a=g.view;a.store=b;if(!a.dataSource.isFeatureStore){a.dataSource=b}if(a.bufferedRenderer){a.bufferedRenderer.bindStore(b)}e.view.store=b;d=e.view.loadMask;if(d&&d.isLoadMask){d.bindStore(b)}e.view.bindStore(g.view.dataSource,false,"dataSource");h.view.blockRefresh=g.view.blockRefresh=false}if(c){h.reconfiguring=g.reconfiguring=true;h.headerCt.removeAll();g.headerCt.removeAll();c=e.processColumns(c,h);h.headerCt.add(c.locked.items);g.headerCt.add(c.normal.items);h.reconfiguring=g.reconfiguring=false;e.syncLockedWidth()}e.refreshCounter=h.view.refreshCounter},afterReconfigureLockable:function(){var a=this.lockedGrid.getView();if(this.refreshCounter===a.refreshCounter){this.view.refresh()}},constructLockableFeatures:function(){var e=this.features,c,d,g,h,b=0,a;if(e){if(!Ext.isArray(e)){e=[e]}g=[];h=[];a=e.length;for(;b0){d.onViewResize(b,null,a);if(c&&(e.getCount()!==c.length)){c.length=0;c.push.apply(c,d.store.getRange(e.startIndex,e.endIndex))}}}},beforeTableLayout:function(a){var b=this.view.body.dom;if(b){a.bodyHeight=b.offsetHeight;a.bodyWidth=b.offsetWidth}},afterTableLayout:function(d){var b=this,a=b.view,c;if(d.bodyHeight&&a.body.dom){delete b.rowHeight;b.refreshSize();c=a.body.dom.offsetHeight;if(c!==d.bodyHeight){b.onViewResize(a,null,a.el.lastBox.height);if(c=b.store.getCount()){b.setBodyTop(0)}else{if(b.bodyTop>b.scrollTop||b.bodyTop+ca){c.position=c.scrollTop=Math.max(a-c.bodyHeight,0);b.setScrollY(c.scrollTop)}if(c.bodyTop>a){b.body.translate(null,c.bodyTop=c.position)}if(b.getScrollable()){c.refreshScroller(b,a)}},refreshScroller:function(c,b){var a=c.getScrollable();if(a){if(a.setElementSize){a.setElementSize()}a.setSize({x:c.headerCt.getTableWidth(),y:b})}},setViewSize:function(l,c){var k=this,o=k.store,m=k.view,p=m.all,g=p.getCount(),b,e,i=k.view.lockingPartner&&k.view.lockingPartner.bufferedRenderer,n=g-l,d,a,h,j;if(i&&!c&&i.view.componentLayoutCounter){if(i.viewSize>l){l=i.viewSize}else{i.setViewSize(l,true)}}n=g-l;if(n){k.scrollTop=m.getScrollY();k.viewSize=l;if(o.isBufferedStore){o.setViewSize(l)}if(g){j=o.getCount();b=p.startIndex;e=Math.min(b+l-1,j-1);if(b===p.startIndex&&e===p.endIndex){if(n<0){k.handleViewScroll(-1)}}else{if(i){i.disable()}if(n<0){if(j>g){o.getRange(p.endIndex+1,e,{callback:function(q,r){h=m.doAdd(q,r);m.fireEvent("itemadd",q,r,h);k.setBodyTop(k.bodyTop)}})}else{k.refreshView(0)}}else{b=p.endIndex-(n-1);e=p.endIndex;a=p.slice(b,e+1);p.removeRange(b,e,true);if(m.hasListeners.itemremove){d=o.getRange(b,e);m.fireEvent("itemremove",d,b,a)}k.setBodyTop(k.bodyTop)}if(i){i.enable()}}}}return l},getViewRange:function(){var b=this,c=b.view.all,a=b.store,d=0;if(c.getCount()){d=c.startIndex}else{if(a.isBufferedStore){if(!a.currentPage){a.currentPage=1}d=c.startIndex=(a.currentPage-1)*(a.pageSize||1);a.currentPage=1}}if(a.data.getCount()){return a.getRange(d,d+(b.viewSize||a.defaultViewSize)-1)}else{return[]}},onReplace:function(j,k,e,g){var h=this,i=h.view,m=i.all,a,d=m.getCount(),c=k+e.length-1,l=g.length-e.length,b=l*h.rowHeight;if(k>=m.startIndex+h.viewSize){h.refreshSize();return}if(d&&c=h.viewSize){m.moveBlock(l);h.refreshSize();a=m.startIndex;if(l>0){h.doNotMirror=true;h.handleViewScroll(-1);h.doNotMirror=false}if(m.startIndex===a){if(m.startIndex){h.setBodyTop(h.bodyTop+=b);i.suspendEvent("scroll");i.scrollBy(0,b);i.resumeEvent("scroll");h.position=h.scrollTop=i.getScrollY()}}else{i.suspendEvent("scroll");i.scrollBy(0,(a-m.startIndex)*h.rowHeight);i.resumeEvent("scroll")}i.refreshSize(m.getCount()!==d);return}if(d&&k>m.endIndex){h.refreshSize();if(l>0){h.onRangeFetched(null,m.startIndex,Math.min(j.getCount(),m.startIndex+h.viewSize)-1,null,true)}i.refreshSize(m.getCount()!==d);return}if(k0?1:-1;if(Math.abs(c)>=20||(g!==d.lastScrollDirection)){d.lastScrollDirection=g;d.handleViewScroll(d.lastScrollDirection)}}},onViewScrollEnd:function(){if(this.needsPointerEventsFix){this.view.body.dom.style.pointerEvents=""}},handleViewScroll:function(h){var e=this,g=e.view.all,a=e.store,i=e.viewSize,c=a.getCount()-1,d,b;if(h===-1){if(g.startIndex){if(e.topOfViewCloseToEdge()){d=Math.max(0,e.getLastVisibleRowIndex()+e.trailingBufferZone-i)}}}else{if(g.endIndexa.scrollTop-(a.numFromEdge*a.rowHeight)}else{return(a.getFirstVisibleRowIndex()-a.view.all.startIndex)i){g=d-i+1}}}if(g===0&&d===0&&h===0){c.doRefreshView([],0,0)}else{a.getRange(g,d,{callback:c.doRefreshView,scope:c})}},doRefreshView:function(h,p,g,r){var n=this,o=n.view,q=o.all,l=q.startIndex,j=q.endIndex,m,k,b=q.getCount(),e,a=p!==q.startIndex,d,c,i;if(o.refreshCounter){if(o.hasListeners.beforerefresh&&o.fireEvent("beforerefresh",o)===false){return}o.refreshing=n.refreshing=true;i=o.saveFocusState();o.clearViewEl(true);o.refreshCounter++;if(h.length){e=o.doAdd(h,p);if(a){m=q.item(l,true);k=q.item(j,true);if(m){c=-m.offsetTop}else{if(k){c=q.last(true).offsetTop-k.offsetTop}}if(c){n.bodyTop=Math.max(n.bodyTop+c,0);n.scrollTop=n.bodyTop?n.scrollTop+c:0}else{n.bodyTop=d=p*n.rowHeight;n.scrollTop=Math.max(d-n.rowHeight*(do.endIndex){x=o.startIndex-h;p.clearViewEl(true);w=p.doAdd(q,h);p.fireEvent("itemadd",q,h,w);for(u=0;uo.endIndex||ea){m=z.scrollTop-z.rowHeight*a}}p.clearViewEl(true);z.teleported=false}if(!o.getCount()){w=p.doAdd(q,h);p.fireEvent("itemadd",q,h,w)}else{if(e>o.endIndex){k=Math.max(h-o.startIndex,0);if(t){y=o.item(o.startIndex+k,true).offsetTop}w=o.scroll(Ext.Array.slice(q,o.endIndex+1-h),1,k);if(t){m=z.bodyTop+y}else{m=v}}else{k=Math.max(o.endIndex-e,0);c=o.startIndex;w=o.scroll(Ext.Array.slice(q,0,o.startIndex-h),-1,k);if(t){m=z.bodyTop-o.item(c,true).offsetTop;if(!o.startIndex){if(m){p.setScrollY(z.position=(z.scrollTop-=m));m=0}}else{if(m<0){y=o.startIndex*z.rowHeight;p.setScrollY(z.position=(z.scrollTop+=y));m=z.bodyTop+y}}}else{m=v}}}z.position=z.scrollTop}if(r&&!l.contains(g)){j=p.actionableMode?p.actionPosition:p.lastFocused;if(j&&j.column){p.onFocusLeave({});j.column.focus()}}m=Math.max(Math.floor(m),0);if(p.positionBody){z.setBodyTop(m)}if(w&&n&&!n.disabled){n.scrollTop=n.position=z.scrollTop;if(n.view.ownerCt.isVisible()){b=n.onRangeFetched(null,h,e,d,true);if(p.ownerGrid.syncRowHeight||(n.variableRowHeight!==t)){z.syncRowHeights(w,b);z.bodyHeight=p.body.dom.offsetHeight}}if(n.bodyTop!==m){n.setBodyTop(m)}n.view.setScrollY(z.scrollTop)}return w},syncRowHeights:function(g,a){var j=this,h=0,l=1,k=[],b=[],d=Ext.grid.locking.RowSynchronizer,c,e;if(g&&a){h=g.length;l=a.length}if(h!==l){g=j.view.all.slice();a=j.view.lockingPartner.all.slice();h=l=g.length}for(c=0;cg||j+h.bodyHeightg||k+i.bodyHeightg){return i.getLastVisibleRowIndex(l,e-1,b,g)}h=m+a[e].offsetHeight;if(h>=g){return e}else{if(e!==c){return i.getLastVisibleRowIndex(e+1,c,b,g)}}}return i.getFirstVisibleRowIndex()+Math.ceil(d/i.rowHeight)},getScrollHeight:function(){var c=this,e=c.view,j=e.all,g=c.store,k=g.getCount(),d=j.getCount(),i,b,a,h;if(!k){return 0}if(!c.hasOwnProperty("rowHeight")){if(d){if(c.variableRowHeight){c.rowHeight=Math.floor(c.bodyHeight/d)}else{i=j.first();b=i.getHeight();if(Ext.isIE8){a=i.getBorderWidth("b");if(a>0){b-=a}}c.rowHeight=b}}else{delete c.rowHeight}}if(c.variableRowHeight){if(j.endIndex===k-1){h=c.bodyTop+c.bodyHeight-1}else{h=Math.floor((k-d)*c.rowHeight)+c.bodyHeight;h+=c.bodyTop-j.startIndex*c.rowHeight}}else{h=Math.floor(k*c.rowHeight)}return(c.scrollHeight=h)},attemptLoad:function(c,a){var b=this;if(b.scrollToLoadBuffer){if(!b.loadTask){b.loadTask=new Ext.util.DelayedTask(b.doAttemptLoad,b,[])}b.loadTask.delay(b.scrollToLoadBuffer,b.doAttemptLoad,b,[c,a])}else{b.doAttemptLoad(c,a)}},cancelLoad:function(){if(this.loadTask){this.loadTask.cancel()}},doAttemptLoad:function(c,a){var b=this;if(!b.destroyed){b.store.getRange(c,a,{loadId:++b.loadId,callback:function(e,h,d,g){if(g.loadId===b.loadId){b.onRangeFetched(e,h,d,g)}},fireEvent:false})}},destroy:function(){var b=this,a=b.view;b.cancelLoad();if(a&&a.el){a.un("scroll",b.onViewScroll,b)}if(b.store){b.unbindStore()}b.viewListeners=b.gridListeners=b.view=b.grid=Ext.destroy(b.viewListeners,b.stretcher,b.gridListeners);Ext.plugin.Abstract.prototype.destroy.call(this)}},0,0,0,0,["plugin.bufferedrenderer"],0,[Ext.grid.plugin,"BufferedRenderer"],function(a){if(Ext.supports.Touch){a.prototype.leadingBufferZone=a.prototype.trailingBufferZone=2;a.prototype.numFromEdge=1}}));(Ext.cmd.derive("Ext.grid.plugin.Editing",Ext.plugin.Abstract,{clicksToEdit:2,triggerEvent:undefined,relayedEvents:["beforeedit","edit","validateedit","canceledit"],defaultFieldUI:"default",defaultFieldXType:"textfield",editStyle:"",constructor:function(a){var b=this;Ext.plugin.Abstract.prototype.constructor.call(this,a);b.mixins.observable.constructor.call(b);b.on("edit",function(c,d){b.fireEvent("afteredit",c,d)})},init:function(a){var b=this,c=a.ownerLockable;b.grid=a;b.view=a.view;b.initEvents();if(a.rendered){b.setup()}else{b.mon(a,{beforereconfigure:b.onBeforeReconfigure,reconfigure:b.onReconfigure,scope:b,beforerender:{fn:b.onBeforeRender,single:true,scope:b}})}a.editorEventRelayers=a.relayEvents(b,b.relayedEvents);if(c){c.editorEventRelayers=c.relayEvents(b,b.relayedEvents)}a.isEditable=true;a.editingPlugin=a.view.editingPlugin=b},onBeforeReconfigure:function(){this.reconfiguring=true},onReconfigure:function(){this.setup();delete this.reconfiguring},onBeforeRender:function(){this.setup()},setup:function(){this.initFieldAccessors(this.grid.getTopLevelColumnManager().getColumns())},destroy:function(){var b=this,a=b.grid;Ext.destroy(b.keyNav);b.clearListeners();if(a){if(a.ownerLockable){Ext.destroy(a.ownerLockable.editorEventRelayers);a.ownerLockable.editorEventRelayers=null}Ext.destroy(a.editorEventRelayers);a.editorEventRelayers=null;a.editingPlugin=a.view.editingPlugin=b.grid=b.view=b.editor=b.keyNav=null}Ext.plugin.Abstract.prototype.destroy.call(this)},getEditStyle:function(){return this.editStyle},initFieldAccessors:function(d){if(d.isGroupHeader){d=d.getGridColumns()}else{if(!Ext.isArray(d)){d=[d]}}var h=this,j,i=d.length,b=function(c,k){return h.getColumnField(this,k)},a=function(){return h.hasColumnField(this)},g=function(c){h.setColumnField(this,c)},e;for(j=0;j0;){g[b]=e[a[b].get](b,d&&!c)}}return g},getHiddenTextArea:function(){var b=this.shared,a;a=b.textArea;if(!a){a=b.textArea=Ext.getBody().createChild({tag:"textarea",tabIndex:-1,style:{position:"absolute",top:"-1000px",width:"1px",height:"1px"}});a.suspendFocusEvents()}return a},onCopy:function(b,a){this.doCutCopy(a,false)},onCut:function(b,a){this.doCutCopy(a,true)},onPaste:function(h,e){var d=this,a=d.shared.data,g=d.getSource(),b,j,c;if(g){for(b=0,j=g.length;b0;){b=g[a];e[b]=e[b]?3:2}}return d.allFormats=e},updateMemory:function(){this.allFormats=null},updateSystem:function(){this.allFormats=null}}},0,0,0,0,0,0,[Ext.plugin,"AbstractClipboard"],0));(Ext.cmd.derive("Ext.grid.plugin.Clipboard",Ext.plugin.AbstractClipboard,{formats:{cell:{get:"getCells"},html:{get:"getCellData"},raw:{get:"getCellData"}},getCellData:function(n,b){var k=this.getCmp(),h=k.getSelectionModel(),i=[],m=n==="raw",g=n==="text",q,o,d,l,a,c,e,p,j;h.getSelected().eachCell(function(r){c=r.column,j=r.column.getView();e=r.record;if(c.ignoreExport){return}if(a!==e){a=e;i.push(p=[])}l=c.dataIndex;if(m){d=e.data[l]}else{q=j.all.item(r.rowIdx);if(!q){q=Ext.fly(j.createRowElement(e,r.rowIdx))}o=q.down(c.getCellInnerSelector());d=o.dom.innerHTML;if(g){d=Ext.util.Format.stripTags(d)}}p.push(d);if(b&&l){e.set(l,null)}});return Ext.util.TSV.encode(i)},getCells:function(i,b){var g=this.getCmp(),d=g.getSelectionModel(),e=[],h,a,c,j;d.getSelected().eachCell(function(k){c=k.record;if(a!==c){a=c;e.push(j={model:c.self,fields:[]})}h=k.column.dataIndex;j.fields.push({name:h,value:c.data[h]});if(b&&h){c.set(h,null)}});return e},getTextData:function(b,a){return this.getCellData(b,a)},putCellData:function(g,m){var o=Ext.util.TSV.decode(g),p,q=o.length,n=q?o[0].length:0,h,k,j=this.getCmp().getView(),a=j.dataSource.getCount()-1,e=j.getVisibleColumnManager().getColumns().length-1,c=j.getNavigationModel(),i=c.getPosition(),l,b,d={};if(!i){j.getSelectionModel().getSelected().eachCell(function(r){i=r;return false})}if(i){i=new Ext.grid.CellContext(j).setPosition(i.record,i.column)}else{i=new Ext.grid.CellContext(j).setPosition(0,0)}b=i.colIdx;for(h=0;h[isFormField]"),h=j.length,d,a,k;for(d=0;de){b.setHeight(g)}else{c.setHeight(e)}}}else{c.dom.style.height=b.dom.style.height=""}},onColumnUnlock:function(c,a){var b=this,d;c=b.grid.ownerLockable;d=c.lockedGrid.visibleColumnManager.getColumns();if(d.length===1){if(d[0]===b.expanderColumn){c.unlock(b.expanderColumn);b.grid=c.normalGrid}else{c.lock(b.expanderColumn,0)}}},onColumnLock:function(c,a){var b=this,e,d;c=b.grid.ownerLockable;e=c.lockedGrid.visibleColumnManager.getColumns();if(e.length===1){b.grid=d=c.lockedGrid;d.headerCt.insert(0,b.expanderColumn)}},getHeaderConfig:function(){var b=this,a=b.grid.ownerLockable;return{width:b.headerWidth,ignoreExport:true,lockable:false,autoLock:true,sortable:false,resizable:false,draggable:false,hideable:false,menuDisabled:true,tdCls:"x-grid-cell-special",innerCls:"x-grid-cell-inner-row-expander",renderer:function(){return''},processEvent:function(i,g,c,k,h,j,d){if((i==="click"&&j.getTarget(".x-grid-row-expander"))||(i==="keydown"&&j.getKey()===j.SPACE)){b.toggleRow(k,d);return b.selectRowOnExpand}},isLocked:function(){return a&&(a.lockedGrid.isVisible()||this.locked)},editRenderer:function(){return" "}}}},0,0,0,0,["plugin.rowexpander"],0,[Ext.grid.plugin,"RowExpander"],0));(Ext.cmd.derive("Ext.grid.property.Grid",Ext.grid.Panel,{alternateClassName:"Ext.grid.PropertyGrid",valueField:"value",nameField:"name",inferTypes:true,enableColumnMove:false,columnLines:true,stripeRows:false,trackMouseOver:false,clicksToEdit:1,enableHdMenu:false,gridCls:"x-property-grid",initComponent:function(){var a=this;a.source=a.source||{};a.addCls(a.gridCls);a.plugins=a.plugins||[];a.plugins.push(new Ext.grid.plugin.CellEditing({clicksToEdit:a.clicksToEdit,startEdit:function(b,c){return this.self.prototype.startEdit.call(this,b,a.valueColumn)}}));a.selModel={type:"cellmodel",onCellSelect:function(b){b.column=a.valueColumn;b.colIdx=a.valueColumn.getVisibleIndex();return this.self.prototype.onCellSelect.call(this,b)}};a.sourceConfig=Ext.apply({},a.sourceConfig);if(!a.store){a.propStore=a.store=new Ext.grid.property.Store(a,a.source)}a.configure(a.sourceConfig);if(a.sortableColumns){a.store.sort("name","ASC")}a.columns=new Ext.grid.property.HeaderContainer(a,a.store);Ext.grid.Panel.prototype.initComponent.call(this);a.getView().walkCells=this.walkCells;a.editors={date:new Ext.grid.CellEditor({field:new Ext.form.field.Date({selectOnFocus:true})}),string:new Ext.grid.CellEditor({field:new Ext.form.field.Text({selectOnFocus:true})}),number:new Ext.grid.CellEditor({field:new Ext.form.field.Number({selectOnFocus:true})}),"boolean":new Ext.grid.CellEditor({field:new Ext.form.field.ComboBox({editable:false,store:[[true,a.headerCt.trueText],[false,a.headerCt.falseText]]})})};a.store.on("update",a.onUpdate,a)},configure:function(b){var h=this,k=h.store,d=0,e=h.store.getCount(),l=h.nameField,m=h.valueField,a,j,c,g;h.configureLegacy(b);if(h.inferTypes){for(;d=b[0]&&a.rowIdx<=b[1]){b=this.getColumnRange();return(a.colIdx>=b[0]&&a.colIdx<=b[1])}}return false},eachRow:function(c,b){var e=this,g=e.getRowRange(),a=new Ext.grid.CellContext(e.view),d;for(d=g[0];d<=g[1];d++){a.setRow(d);if(c.call(b||e,a.record)===false){return}}},eachColumn:function(d,c){var e=this,a=e.getColumnRange(),b=new Ext.grid.CellContext(e.view),g;for(g=a[0];g<=a[1];g++){b.setColumn(g);if(d.call(c||e,b.column,g)===false){return}}},eachCell:function(d,c){var g=this,h=g.getRowRange(),a=g.getColumnRange(),b=new Ext.grid.CellContext(g.view),e,i;for(e=h[0];e<=h[1];e++){b.setRow(e);for(i=a[0];i<=a[1];i++){b.setColumn(i);if(d.call(c||g,b,i,e)===false){return}}}},getFirstRowIndex:function(){return this.startCell?Math.min(this.startCell.rowIdx,this.endCell.rowIdx):0},getLastRowIndex:function(){return this.startCell?Math.max(this.startCell.rowIdx,this.endCell.rowIdx):-1},getFirstColumnIndex:function(){return this.startCell?Math.min(this.startCell.colIdx,this.endCell.colIdx):0},getLastColumnIndex:function(){return this.startCell?Math.max(this.startCell.colIdx,this.endCell.colIdx):-1},privates:{clear:function(){var b=this,a=b.view;b.eachCell(function(c){a.onCellDeselect(c)});b.startCell=b.endCell=null},setRangeStart:function(b,a){this.startCell=(this.endCell=b.clone()).clone();this.view.onCellSelect(b)},setRangeEnd:function(b){var k=this,i,h,c,j,g,n,a,e,l=k.view,o=l.all,m=new Ext.grid.CellContext(l),d=l.getVisibleColumnManager().getColumns().length-1;k.endCell=b.clone();i=k.getRange();h=k.lastRange||i;c=Math.max(Math.min(i[0][1],h[0][1]),o.startIndex);j=Math.min(Math.max(i[1][1],h[1][1]),o.endIndex);g=Math.min(i[0][0],h[0][0]);n=Math.min(Math.max(i[1][0],h[1][0]),d);for(a=c;a<=j;a++){for(e=g;e<=n;e++){m.setPosition(a,e);if(ai[1][1]||ei[1][0]){l.onCellDeselect(m)}else{l.onCellSelect(m)}}}k.lastRange=i},extendRange:function(b){var c=this,a;if(b[b.type]<0){a=c.endCell.clone().setPosition(c.getLastRowIndex(),c.getLastColumnIndex());c.startCell=b.start.clone();c.setRangeEnd(a);c.view.getNavigationModel().setPosition(b.start)}else{c.startCell=c.startCell.setPosition(c.getFirstRowIndex(),c.getFirstColumnIndex());c.setRangeEnd(b.end);c.view.getNavigationModel().setPosition(b.end)}},getRange:function(){return[[this.getFirstColumnIndex(),this.getFirstRowIndex()],[this.getLastColumnIndex(),this.getLastRowIndex()]]},getRangeSize:function(){return this.getCount()},getCount:function(){var a=this.getRange();return(a[1][0]-a[0][0]+1)*(a[1][1]-a[0][1]+1)},selectAll:function(){var b=this,a=b.view;b.clear();b.setRangeStart(new Ext.grid.CellContext(a).setPosition(0,0));b.setRangeEnd(new Ext.grid.CellContext(a).setPosition(a.dataSource.getCount()-1,a.getVisibleColumnManager().getColumns().length-1))},isAllSelected:function(){var b=this.rangeStart,a=this.rangeEnd;if(b){if(!b.colIdx&&!b.rowIdx){return a.colIdx===a.view.getVisibleColumnManager().getColumns().length-1&&a.rowIdx===a.view.dataSource.getCount-1}}return false},getColumnRange:function(){return[this.getFirstColumnIndex(),this.getLastColumnIndex()]},getRowRange:function(){return[this.getFirstRowIndex(),this.getLastRowIndex()]},onSelectionFinish:function(){var a=this;if(a.getCount()){a.view.getSelectionModel().onSelectionFinish(a,new Ext.grid.CellContext(a.view).setPosition(a.getFirstRowIndex(),a.getFirstColumnIndex()),new Ext.grid.CellContext(a.view).setPosition(a.getLastRowIndex(),a.getLastColumnIndex()))}else{a.view.getSelectionModel().onSelectionFinish(a)}}}},0,0,0,0,0,0,[Ext.grid.selection,"Cells"],0));(Ext.cmd.derive("Ext.grid.selection.Columns",Ext.grid.selection.Selection,{type:"columns",isColumns:true,clone:function(){var c=this,a=new c.self(c.view),b=c.selectedColumns;if(b){a.selectedColumns=Ext.Array.slice(b)}return a},eachRow:function(c,b){var a=this.selectedColumns;if(a&&a.length){this.view.dataSource.each(c,b||this)}},eachColumn:function(h,g){var j=this,b=j.view,e=j.selectedColumns,a,d,c=new Ext.grid.CellContext(b);if(e){a=e.length;for(d=0;d=d[0]&&a<=d[1]}return b},getCount:function(){var e=this,g=e.selectedRecords,a=g?g.getCount():0,c=e.getRange(),d,b=e.view.dataSource;for(d=c[0];d<=c[1];d++){if(!g||!g.byInternalId.get(b.getAt(d).internalId)){a++}}return a},getRecords:function(){var a=this.selectedRecords;return a?a.getRange():[]},selectAll:function(){var a=this;a.clear();a.setRangeStart(0);a.setRangeEnd(a.view.dataSource.getCount()-1);a.addRange();a.allSelected=true},getFirstRowIndex:function(){return this.getCount()?this.view.dataSource.indexOf(this.selectedRecords.first()):0},getLastRowIndex:function(){return this.getCount()?this.view.dataSource.indexOf(this.selectedRecords.first()):-1},eachRow:function(b,a){var c=this.selectedRecords;if(c){c.each(b,a||this)}},eachColumn:function(e,d){var c=this.view.getVisibleColumnManager().getColumns(),a=c.length,b;if(this.selectedRecords){for(b=0;bg[1]){if(d&&(b=d.byInternalId.get(j.getAt(a).internalId))){d.remove(b)}i.onRowDeselect(a)}else{i.onRowSelect(a)}}h.lastRange=g},extendRange:function(b){var d=this,a=d.view.dataSource,c;for(c=b.start.rowIdx;c<=b.end.rowIdx;c++){d.add(a.getAt(c))}},getRange:function(){var b=this.rangeStart,a=this.rangeEnd;if(b==null){return[0,-1]}else{if(b<=a){return[b,a]}}return[a,b]},getRangeSize:function(){var a=this.getRange();return a[1]-a[0]+1},createRecordCollection:function(){var b=this.view.dataSource,a=new Ext.util.Collection({rootProperty:"data",extraKeys:{byInternalId:{rootProperty:false,property:"internalId"}},sorters:[function(d,c){return b.indexOf(d)-b.indexOf(c)}]});return a},addRange:function(){var c=this,a,b;if(c.rangeStart!=null){a=c.getRange();b=c.selectedRecords||(c.selectedRecords=c.createRecordCollection());c.view.dataSource.getRange(a[0],a[1],{callback:function(d){b.add.apply(b,d)}});c.setRangeStart(c.lastRange=null)}},onSelectionFinish:function(){var b=this,a=b.getContiguousSelection();if(a){b.view.getSelectionModel().onSelectionFinish(b,new Ext.grid.CellContext(b.view).setPosition(a[0],0),new Ext.grid.CellContext(b.view).setPosition(a[1],b.view.getVisibleColumnManager().getColumns().length-1))}else{b.view.getSelectionModel().onSelectionFinish(b)}},getContiguousSelection:function(){var b=this.view.dataSource,d,a,c;if(this.selectedRecords){d=Ext.Array.sort(this.selectedRecords.getRange(),function(g,e){return b.indexOf(g)-b.indexOf(e)});a=d.length;if(a){for(c=1;cb+c.el.getHeight(true)-15){if(i.extendY){a[1]=3;d.start()}}else{if(i.lastXY[1]g+c.el.getWidth(true)-15){if(i.extendX){a[0]=3;d.start()}}else{if(i.lastXY[0]h.lastPos.rowIdx&&h.extendY){h.extensionDescriptor={type:"rows",start:d.setRow(h.lastPos.rowIdx+1),end:c.setRow(b.rowIdx),rows:b.rowIdx-h.lastPos.rowIdx,mousePosition:h.lastXY};h.mask.dom.style.borderTopWidth="0";g.x=l.x;g.y=l.bottom;g.width=l.right-l.left;g.height=e.getRegion().bottom-l.bottom}else{if(b.colIdxh.lastPos.colIdx&&h.extendX){h.extensionDescriptor={type:"columns",start:d.setColumn(h.lastPos.colIdx+1),end:c.setColumn(b.colIdx),columns:b.colIdx-h.lastPos.colIdx,mousePosition:h.lastXY};h.mask.dom.style.borderLeftWidth="0";g.x=l.right;g.y=l.top;g.width=e.getRegion().right-l.right;g.height=l.bottom-l.top}else{h.extensionDescriptor=null}}}}if(j.ownerGrid.hasListeners.selectionextenderdrag){j.ownerGrid.fireEvent("selectionextenderdrag",j.ownerGrid,j.getSelectionModel().getSelected(),h.extensionDescriptor)}if(h.extensionDescriptor){h.mask.show();h.mask.setBox(g)}else{h.mask.hide()}},destroy:function(){var a=this;Ext.destroy(a.gridListeners,a.viewListeners,a.mask,a.handle);Ext.dd.DragTracker.prototype.destroy.call(this)}},1,0,0,0,0,0,[Ext.grid.selection,"SelectionExtender"],0));(Ext.cmd.derive("Ext.grid.selection.SpreadsheetModel",Ext.selection.Model,{isSpreadsheetModel:true,config:{columnSelect:{$value:false,lazy:true},cellSelect:{$value:true,lazy:true},rowSelect:{$value:true,lazy:true},dragSelect:{$value:true,lazy:true},selected:null,extensible:{$value:true,lazy:true}},checkboxSelect:false,checkboxColumnIndex:0,showHeaderCheckbox:true,checkboxHeaderWidth:24,rowNumbererHeaderWidth:46,columnSelectCls:"x-ssm-column-select",rowNumbererHeaderCls:"x-ssm-row-numberer-hd",checkerOnCls:"x-grid-hd-checker-on",tdCls:"x-grid-cell-special x-grid-cell-row-checker",bindComponent:function(a){var c=this,b,d;if(c.view!==a){if(c.view){c.navigationModel=null;Ext.destroy(c.viewListeners,c.navigationListeners)}c.view=a;if(a){c.getCellSelect();d=a.ownerGrid.lockedGrid;if(d){c.hasLockedHeader=true;c.onViewCreated(d,d.getView())}else{a.grid.on({viewcreated:c.onViewCreated,scope:c,single:true})}c.gridListeners=a.ownerGrid.on({columnschanged:c.onColumnsChanged,columnmove:c.onColumnMove,scope:c,destroyable:true});b=c.getViewListeners();b.scope=c;b.destroyable=true;c.viewListeners=a.on(b);c.navigationModel=a.getNavigationModel();c.navigationListeners=c.navigationModel.on({navigate:c.onNavigate,scope:c,destroyable:true});if(c.getColumnSelect()){a.ownerGrid.addCls(c.columnSelectCls)}}}},getCheckboxHeaderConfig:function(){var a=this,b=a.showHeaderCheckbox!==false;return{ignoreExport:true,isCheckerHd:b,text:" ",clickTargetName:"el",width:a.checkboxHeaderWidth,sortable:false,draggable:false,resizable:false,hideable:false,menuDisabled:true,dataIndex:"",tdCls:a.tdCls,cls:b?"x-column-header-checkbox ":"",defaultRenderer:a.checkboxRenderer.bind(a),editRenderer:" ",locked:a.hasLockedHeader}},checkboxRenderer:function(){return''},onHeaderClick:function(j,k,h){var d=this,g=d.selected,a,b,c;if(k===d.numbererColumn||k===d.checkColumn){h.stopEvent();if(!g||!g.isAllSelected()){d.selectAll()}else{d.deselectAll()}d.updateHeaderState();d.lastColumnSelected=null}else{if(d.columnSelect){if(h.shiftKey&&g&&g.lastColumnSelected){g.clear();a=this.view.ownerGrid.getVisibleColumnManager();b=Ext.Array.sort([a.indexOf(g.lastColumnSelected),a.indexOf(k)],Ext.Array.numericSortFn);for(c=b[0];c<=b[1];c++){d.selectColumn(a.getHeaderAtIndex(c),true)}}else{if(d.isColumnSelected(k)){d.deselectColumn(k);d.selected.lastColumnSelected=null}else{d.selectColumn(k,h.ctrlKey);d.selected.lastColumnSelected=k}}}}},updateHeaderState:function(){var e=this,c=e.view.dataSource,i=c.getCount(),b=e.views,g=e.selected,h=g&&g.isRows&&!c.isBufferedStore&&i>0&&(i===g.getCount()),d=e.checkColumn,a=e.checkerOnCls;if(b&&b.length){if(d){if(h){d.addCls(a)}else{d.removeCls(a)}}}},onBeforeReconfigure:function(d,a,c,g,b){var e=this;if(c){Ext.suspendLayouts();if(e.numbererColumn){e.numbererColumn.ownerCt.remove(e.numbererColumn,false);c.unshift(e.numbererColumn)}if(e.checkColumn){e.checkColumn.ownerCt.remove(e.checkColumn,false);c.unshift(e.checkColumn)}Ext.resumeLayouts()}},getCellContext:function(a,b){return new Ext.grid.CellContext(this.view.ownerGrid.getView()).setPosition(a,b)},select:function(b,m,l){var h=this,a=h.selected,j=h.view,k=j.dataSource,g,d,e,c=false;if(!a||!a.isRows||a.view!==j){h.resetSelection(true);a=h.selected=new Ext.grid.selection.Rows(j)}else{if(!m){a.clear()}}if(!Ext.isArray(b)){b=[b]}g=b.length;for(d=0;d1&&d.type==="click"){return}if(!(g.cellSelect||g.columnSelect||g.rowSelect)||!e.record||d.type==="mousedown"){return}if(d.ctrlKey&&d.keyCode===d.A){if(!a||a.getCount()<2){g.selectAll()}else{g.deselectAll()}g.updateHeaderState();return}if(d.shiftKey){if(h.column===g.numbererColumn||h.column===g.checkColumn||!g.cellSelect||(a&&a.isRows)){if(g.rowSelect){if(!a||!a.isRows||a.view!==i){g.resetSelection(true);a=g.selected=new Ext.grid.selection.Rows(i)}if(!a.getRangeSize()){a.setRangeStart(e.previousRecordIndex||0)}a.setRangeEnd(e.recordIndex);a.addRange();b=true}}else{if(g.cellSelect){if(!a||!a.isCells||a.view!==i){g.resetSelection(true);a=g.selected=new Ext.grid.selection.Cells(i)}if(!a.getRangeSize()){a.setRangeStart(e.previousPosition||g.getCellContext(0,0))}a.setRangeEnd(h);b=true}}}else{if(h.column===g.numbererColumn||h.column===g.checkColumn||!g.cellSelect){if(g.rowSelect){if(!a||!a.isRows||a.view!==i){g.resetSelection(true);a=g.selected=new Ext.grid.selection.Rows(i)}if(d.ctrlKey||h.column===g.checkColumn){if(a.contains(c)){a.remove(c)}else{a.add(c)}}else{a.clear();a.add(c)}b=true}}else{if(g.cellSelect){if(!a||!a.isCells||a.view!==i){g.resetSelection(true);g.selected=a=new Ext.grid.selection.Cells(i)}else{a.clear()}a.setRangeStart(h);b=true}}}if(b){if(a.isRows){g.updateHeaderState()}g.fireSelectionChange()}},isRowSelected:function(a){var b=this,c=b.selected;if(c&&c.isRows){a=Ext.isNumber(a)?b.store.getAt(a):a;return c.contains(a)}else{return false}},isColumnSelected:function(a){var b=this,c=b.selected;if(c&&c.isColumns){return c.contains(a)}else{return false}},isCellSelected:function(a,g,c){var d=this,b,e=d.selected;a=a.ownerGrid.view;if(e){if(e.isColumns){if(typeof c==="number"){c=a.getVisibleColumnManager().getColumns()[c]}return e.contains(c)}if(e.isCells){b=new Ext.grid.CellContext(a).setPosition({row:g,column:c});return e.contains(b)}}return false},applySelected:function(a){return a},updateSelected:function(g,h){var c,e,b,d,a;if(h){h.clear()}if(g&&g.getCount()){c=g.view;if(g.isRows){g.eachRow(c.onRowSelect,c)}else{if(g.isColumns){e=g.getColumns();b=e.length;if(b){a=new Ext.grid.CelContext(c);c.store.each(function(i){a.setRow(i);for(d=0;d0);if(j){t.widthModel=t.heightModel=null;b=v.getSizeModel(l&&l.widthModel.pairsByHeightOrdinal[l.heightModel.ordinal]);if(h){t.sizeModel=b}t.widthModel=b.width;t.heightModel=b.height;if(l&&!t.isComponentChild){if(p.needsItemSize||!v.liquidLayout){l.remainingChildDimensions+=2}else{if(t.widthModel.calculated){++l.remainingChildDimensions}if(t.heightModel.calculated){++l.remainingChildDimensions}}}}else{if(a){t.recoverProp("x",a,d);t.recoverProp("y",a,d);if(t.widthModel.calculated){t.recoverProp("width",a,d)}else{if("width" in a){++s}}if(t.heightModel.calculated){t.recoverProp("height",a,d)}else{if("height" in a){++s}}if(l&&!t.isComponentChild){l.remainingChildDimensions+=s}}}if(a&&p&&p.manageMargins){t.recoverProp("margin-top",a,d);t.recoverProp("margin-right",a,d);t.recoverProp("margin-bottom",a,d);t.recoverProp("margin-left",a,d)}if(c){k=c.heightModel;r=c.widthModel;if(r&&k&&g&&w){if(g.shrinkWrap&&w.shrinkWrap){if(r.constrainedMax&&k.constrainedMin){k=null}}}if(r){t.widthModel=r}if(k){t.heightModel=k}if(c.state){Ext.apply(t.state,c.state)}}return u},initContinue:function(e){var h=this,d=h.ownerCtContext,a=h.target,c=h.widthModel,g=a.getInherited(),b;if(c.fixed){g.inShrinkWrapTable=false}else{delete g.inShrinkWrapTable}if(e){if(d&&c.shrinkWrap){b=d.isBoxParent?d:d.boxParent;if(b){b.addBoxChild(h)}}else{if(c.natural){h.boxParent=d}}}return e},initDone:function(d){var b=this,a=b.props,c=b.state;if(b.remainingChildDimensions===0){a.containerChildrenSizeDone=true}if(d){a.containerLayoutDone=true}if(b.boxChildren&&b.boxChildren.length&&b.widthModel.shrinkWrap){b.el.setWidth(10000);c.blocks=(c.blocks||0)+1}},initAnimation:function(){var b=this,c=b.target,a=b.ownerCtContext;if(a&&a.isTopLevel){b.animatePolicy=c.ownerLayout.getAnimatePolicy(b)}else{if(!a&&c.isCollapsingOrExpanding&&c.animCollapse){b.animatePolicy=c.componentLayout.getAnimatePolicy(b)}}if(b.animatePolicy){b.context.queueAnimation(b)}},addBlock:function(b,d,e){var c=this,g=c[b]||(c[b]={}),a=g[e]||(g[e]={});if(!a[d.id]){a[d.id]=d;++d.blockCount;++c.context.blockCount}},addBoxChild:function(d){var c=this,b,a=d.widthModel;d.boxParent=this;d.measuresBox=a.shrinkWrap?d.hasRawContent:a.natural;if(d.measuresBox){b=c.boxChildren;if(b){b.push(d)}else{c.boxChildren=[d]}}},addPositionStyles:function(d,b){var a=b.x,e=b.y,c=0;if(a!==undefined){d.left=a+"px";++c}if(e!==undefined){d.top=e+"px";++c}return c},addTrigger:function(g,h){var e=this,a=h?"domTriggers":"triggers",i=e[a]||(e[a]={}),b=e.context,d=b.currentLayout,c=i[g]||(i[g]={});if(!c[d.id]){c[d.id]=d;++d.triggerCount;c=b.triggers[h?"dom":"data"];(c[d.id]||(c[d.id]=[])).push({item:this,prop:g});if(e.props[g]!==undefined){if(!h||!(e.dirty&&(g in e.dirty))){++d.firedTriggers}}}},boxChildMeasured:function(){var b=this,c=b.state,a=(c.boxesMeasured=(c.boxesMeasured||0)+1);if(a===b.boxChildren.length){c.clearBoxWidth=1;++b.context.progressCount;b.markDirty()}},borderNames:["border-top-width","border-right-width","border-bottom-width","border-left-width"],marginNames:["margin-top","margin-right","margin-bottom","margin-left"],paddingNames:["padding-top","padding-right","padding-bottom","padding-left"],trblNames:["top","right","bottom","left"],cacheMissHandlers:{borderInfo:function(a){var b=a.getStyles(a.borderNames,a.trblNames);b.width=b.left+b.right;b.height=b.top+b.bottom;return b},marginInfo:function(a){var b=a.getStyles(a.marginNames,a.trblNames);b.width=b.left+b.right;b.height=b.top+b.bottom;return b},paddingInfo:function(b){var a=b.frameBodyContext||b,c=a.getStyles(b.paddingNames,b.trblNames);c.width=c.left+c.right;c.height=c.top+c.bottom;return c}},checkCache:function(a){return this.cacheMissHandlers[a](this)},clearAllBlocks:function(a){var c=this[a],b;if(c){for(b in c){this.clearBlocks(a,b)}}},clearBlocks:function(c,g){var h=this[c],b=h&&h[g],d,e,a;if(b){delete h[g];d=this.context;for(a in b){e=b[a];--d.blockCount;if(!--e.blockCount&&!e.pending&&!e.done){d.queueLayout(e)}}}},block:function(a,b){this.addBlock("blocks",a,b)},domBlock:function(a,b){this.addBlock("domBlocks",a,b)},fireTriggers:function(b,g){var h=this[b],d=h&&h[g],c=this.context,e,a;if(d){for(a in d){e=d[a];++e.firedTriggers;if(!e.done&&!e.blockCount&&!e.pending){c.queueLayout(e)}}}},flush:function(){var b=this,a=b.dirty,c=b.state,d=b.el;b.dirtyCount=0;if("attributes" in b){d.set(b.attributes);delete b.attributes}if("innerHTML" in b){d.innerHTML=b.innerHTML;delete b.innerHTML}if(c&&c.clearBoxWidth){c.clearBoxWidth=0;b.el.setStyle("width",null);if(!--c.blocks){b.context.queueItemLayouts(b)}}if(a){delete b.dirty;b.writeProps(a,true)}},flushAnimations:function(){var o=this,c=o.previousSize,l,n,e,h,g,d,i,m,k,a,b;if(c){l=o.target;n=l.getAnimationProps();e=n.duration;h=Ext.Object.getKeys(o.animatePolicy);g=Ext.apply({},{from:{},to:{},duration:e||Ext.fx.Anim.prototype.duration},n);for(d=0,i=0,m=h.length;i0},runLayout:function(b){var a=this,c=a.getCmp(b.owner);b.pending=false;if(c.state.blocks){return}b.done=true;++b.calcCount;++a.calcCount;b.calculate(c);if(b.done){a.layoutDone(b);if(b.completeLayout){a.queueCompletion(b)}if(b.finalizeLayout){a.queueFinalize(b)}}else{if(!b.pending&&!b.invalid&&!(b.blockCount+b.triggerCount-b.firedTriggers)){a.queueLayout(b)}}},setItemSize:function(h,g,b){var d=h,a=1,c,e;if(h.isComposite){d=h.elements;a=d.length;h=d[0]}else{if(!h.dom&&!h.el){a=d.length;h=d[0]}}for(e=0;e0){b.insert(0,l)}}}}},getItemsRenderTree:function(a){this.beforeRenderItems(a);return Ext.layout.container.VBox.prototype.getItemsRenderTree.apply(this,arguments)},renderItems:function(a,b){this.beforeRenderItems(a);Ext.layout.container.VBox.prototype.renderItems.apply(this,arguments)},configureItem:function(a){Ext.layout.container.VBox.prototype.configureItem.apply(this,arguments);a.ignoreHeaderBorderManagement=true;a.animCollapse=false;if(this.fill){a.flex=1}},beginLayout:function(a){Ext.layout.container.VBox.prototype.beginLayout.apply(this,arguments);this.innerCt.dom.setAttribute("role","tablist");this.innerCt.dom.setAttribute("aria-multiselectable",true);this.updatePanelClasses(a)},updatePanelClasses:function(e){var c=e.visibleItems,d=c.length,a=true,b,h,g;for(b=0;b1){a.processing=true;b.collapse();a.processing=false}},onRemove:function(a,d){var c=this,b;Ext.layout.container.VBox.prototype.onRemove.apply(this,arguments);if(!c.owner.destroying&&!c.multi&&!a.collapsed){b=c.owner.items.first();if(b){b.expand()}}},getExpanded:function(h){var b=this.owner.items.items,a=b.length,d=0,c=[],g,e;for(;dc){c=e.weight}}},this);return c},getSplitterTarget:function(b){var a=b.collapseTarget;if(a&&a.collapsed){return a.placeholder||a}return a},isItemBoxParent:function(a){return true},isItemShrinkWrap:function(a){return true},insertSplitter:function(d,c,g,b){var h=d.region,e=Ext.apply({xtype:"bordersplitter",collapseTarget:d,id:d.id+"-splitter",hidden:g,canResize:d.splitterResize!==false,splitterFor:d,synthetic:true},b),a=c+((h==="south"||h==="east")?0:1);if(d.collapseMode==="mini"){e.collapsedCls=d.collapsedCls}d.splitter=this.owner.add(a,e)},getMoveAfterIndex:function(b){var a=Ext.layout.container.Container.prototype.getMoveAfterIndex.apply(this,arguments);if(b.splitter){a++}return a},moveItemBefore:function(a,c){var b;if(c&&c.splitter){b=c.region;if(b==="south"||b==="east"){c=c.splitter}}return Ext.layout.container.Container.prototype.moveItemBefore.call(this,a,c)},onAdd:function(j,d){var g=this,c=j.placeholderFor,i=j.region,e,h,a,b;Ext.layout.container.Container.prototype.onAdd.apply(this,arguments);if(i){Ext.apply(j,g.regionFlags[i]);if(g.owner.isViewport){j.isViewportBorderChild=true}if(j.initBorderRegion){j.initBorderRegion()}e=i==="center";if(e){g.centerRegion=j}else{h=j.split;a=!!j.hidden;if(typeof h==="object"){b=h;h=true}if((j.isHorz||j.isVert)&&(h||j.collapseMode==="mini")){g.insertSplitter(j,d,a||!h,b)}}if(!e&&!j.hasOwnProperty("collapseMode")){j.collapseMode=g.panelCollapseMode}if(!j.hasOwnProperty("animCollapse")){if(j.collapseMode!=="placeholder"){j.animCollapse=false}else{j.animCollapse=g.panelCollapseAnimate}}}else{if(c){Ext.apply(j,g.regionFlags[c.region]);j.region=c.region;j.weight=c.weight}}},onDestroy:function(){this.centerRegion=null;Ext.layout.container.Container.prototype.onDestroy.call(this)},onRemove:function(b,d){var e=this,i=b.region,g=b.splitter,a=e.owner,h=a.destroying,c;if(i){if(b.isCenter){e.centerRegion=null}delete b.isCenter;delete b.isHorz;delete b.isVert;if(g&&!a.destroying){a.doRemove(g,true)}delete b.splitter}Ext.layout.container.Container.prototype.onRemove.apply(this,arguments);if(!h&&!d&&b.rendered){c=b.getEl();if(c){c.setStyle("top","");c.setStyle(e.horzPositionProp,"")}}},regionMeta:{center:{splitterDelta:0},north:{splitterDelta:1},south:{splitterDelta:-1},west:{splitterDelta:1},east:{splitterDelta:-1}},regionFlags:{center:{isCenter:true,isHorz:false,isVert:false},north:{isCenter:false,isHorz:false,isVert:true,collapseDirection:"top"},south:{isCenter:false,isHorz:false,isVert:true,collapseDirection:"bottom"},west:{isCenter:false,isHorz:true,isVert:false,collapseDirection:"left"},east:{isCenter:false,isHorz:true,isVert:false,collapseDirection:"right"}},setupSplitterNeighbors:function(m){var p={},e=m.length,o=this.touchedRegions,h,g,a,l,d,k,n,b,c;for(h=0;h{%this.renderBody(out, values)%}'],targetElCls:"x-center-target",beginLayout:function(b){var k=this,c=k.percentRe,g,j,d,a,l,h,e;Ext.layout.container.Fit.prototype.beginLayout.call(this,b);g=b.childItems;for(d=0,j=g.length;d style="border-spacing:{itemSpacing}px">
    style="width:{labelWidth}">
    ',afterBodyTpl:"",getRenderData:function(){var c=this,a=c.labelWidth,b=c.formWrapCls,d=Ext.layout.container.Auto.prototype.getRenderData.call(this);if(a){if(typeof a==="number"){a+="px"}d.labelWidth=a;b+=" "+c.formWrapSizedLabelCls}else{b+=" "+c.formWrapAutoLabelCls}d.formWrapCls=b;d.formColGroupCls=c.formColGroupCls;d.formColumnCls=c.formColumnCls;d.formLabelColumnCls=c.formLabelColumnCls;return d},getRenderTarget:function(){return this.formWrap}},0,0,0,0,["layout.form"],0,[Ext.layout.container,"Form",Ext.layout,"FormLayout"],0));(Ext.cmd.derive("Ext.menu.ColorPicker",Ext.menu.Menu,{hideOnClick:true,pickerId:null,initComponent:function(){var b=this,a=Ext.apply({},b.initialConfig);delete a.listeners;Ext.apply(b,{plain:true,showSeparator:false,bodyPadding:0,items:Ext.applyIf({cls:"x-menu-color-item",margin:0,id:b.pickerId,xtype:"colorpicker"},a)});Ext.menu.Menu.prototype.initComponent.apply(this,arguments);b.picker=b.down("colorpicker");b.relayEvents(b.picker,["select"]);if(b.hideOnClick){b.on("select",b.hidePickerOnSelect,b)}},hidePickerOnSelect:function(){Ext.menu.Manager.hideAll()}},0,["colormenu"],["component","box","container","panel","menu","colormenu"],{component:true,box:true,container:true,panel:true,menu:true,colormenu:true},["widget.colormenu"],0,[Ext.menu,"ColorPicker"],0));(Ext.cmd.derive("Ext.menu.DatePicker",Ext.menu.Menu,{ariaRole:"dialog",ariaLabel:"Date picker",hideOnClick:true,pickerId:null,enableFocusableContainer:false,initComponent:function(){var c=this,a,b;if(c.pickerCfg){b=Ext.apply({cls:"x-menu-date-item",margin:0,border:false,id:c.pickerId,xtype:"datepicker"},c.pickerCfg)}else{a=Ext.apply({},c.initialConfig);delete a.listeners;b=Ext.applyIf({cls:"x-menu-date-item",margin:0,border:false,id:c.pickerId,xtype:"datepicker"},a)}Ext.apply(c,{showSeparator:false,plain:true,bodyPadding:0,items:[b]});Ext.menu.Menu.prototype.initComponent.call(this);c.picker=c.down("datepicker");c.relayEvents(c.picker,["select"]);if(c.hideOnClick){c.on("select",c.hidePickerOnSelect,c)}},onEscapeKey:function(a){if(this.floating&&this.ownerCmp&&this.ownerCmp.focus){this.ownerCmp.focus()}},hidePickerOnSelect:function(){Ext.menu.Manager.hideAll()}},0,["datemenu"],["component","box","container","panel","menu","datemenu"],{component:true,box:true,container:true,panel:true,menu:true,datemenu:true},["widget.datemenu"],0,[Ext.menu,"DatePicker"],0));(Ext.cmd.derive("Ext.panel.Pinnable",Ext.Mixin,{mixinId:"pinnable",pinnable:true,pinnedTip:"Unpin this item",unpinnedTip:"Pin this item",initPinnable:function(){var b=this,a=b.isPinned();b.addTool(b.pinTool=Ext.widget({xtype:"tool",type:a?"unpin":"pin",callback:"togglePin",scope:b,tooltip:a?b.pinnedTip:b.unpinnedTip}))},isPinned:function(){return !this.floating},setPinned:function(b){var c=this,a;if(b!==c.isPinned()){a=[c,b];if(c.fireEventArgs("beforepinchange",a)!==false){c.updatePinned(b);c.fireEventArgs("pinchange",a)}}},togglePin:function(){this.setPinned(!this.isPinned())},updatePinned:function(b){var c=this,a=c.pinTool;a.setTooltip(b?c.pinnedTip:c.unpinnedTip);a.setType(b?"unpin":"pin")}},0,0,0,0,0,0,[Ext.panel,"Pinnable"],0));(Ext.cmd.derive("Ext.plugin.Manager",Ext.Base,{alternateClassName:["Ext.PluginManager","Ext.PluginMgr"],singleton:true,typeName:"ptype",create:function(b,e,d){var a,c;if(b.init){a=b}else{if(d){b=Ext.apply({},b);b.cmp=d}else{d=b.cmp}if(b.xclass){a=Ext.create(b)}else{c="plugin."+(b.ptype||e);a=Ext.ClassManager.instantiateByAlias(c,b)}}if(a&&d&&a.setCmp&&!a.setCmpCalled){a.setCmp(d);a.setCmpCalled=true}return a}},0,0,0,0,0,0,[Ext.plugin,"Manager",Ext,"PluginManager",Ext,"PluginMgr"],0));(Ext.cmd.derive("Ext.resizer.BorderSplitterTracker",Ext.resizer.SplitterTracker,{getPrevCmp:null,getNextCmp:null,calculateConstrainRegion:function(){var z=this,a=z.splitter,o=a.collapseTarget,d=a.defaultSplitMin,h=a.vertical?"Width":"Height",c="min"+h,v="max"+h,j="get"+h,u=a.neighbors,e=u.length,n=o.el.getBox(),g=n.x,p=n.y,y=n.right,k=n.bottom,r=a.vertical?(y-g):(k-p),x,l,s,m,w,t,q,b;m=(o[c]||Math.min(r,d))-r;w=o[v];if(!w){w=1000000000}else{w-=r}b=r;for(x=0;xq){w=q}}if(w-m<2){return null}n=new Ext.util.Region(p,y,k,g);z.constraintAdjusters[z.getCollapseDirection()](n,m,w,a);z.dragInfo={minRange:m,maxRange:w,targetSize:b};return n},constraintAdjusters:{left:function(c,a,b,d){c[0]=c.x=c.left=c.right+a;c.right+=b+d.getWidth()},top:function(c,a,b,d){c[1]=c.y=c.top=c.bottom+a;c.bottom+=b+d.getHeight()},bottom:function(c,a,b,d){c.bottom=c.top-a;c.top-=b+d.getHeight()},right:function(c,a,b,d){c.right=c.left-a;c[0]=c.x=c.left=c.x-b+d.getWidth()}},onBeforeStart:function(k){var g=this,h=g.splitter,d=h.collapseTarget,b=h.neighbors,c=b.length,a,j;if(d.collapsed){return false}for(a=0;ac){d.minWidth=d.el.getWidth()*a}else{d.minHeight=d.el.getHeight()*c}}if(d.throttle){e=Ext.Function.createThrottled(function(){Ext.resizer.ResizeTracker.prototype.resize.apply(d,arguments)},d.throttle);d.resize=function(h,i,g){if(g){Ext.resizer.ResizeTracker.prototype.resize.apply(d,arguments)}else{e.apply(null,arguments)}}}},onBeforeStart:function(a){this.startBox=this.target.getBox()},getProxy:function(){var a=this;if(!a.dynamic&&!a.proxy){a.proxy=a.createProxy(a.target||a.el);a.hideProxy=true}if(a.proxy){a.proxy.show();return a.proxy}},createProxy:function(c){var b,a=this.proxyCls;if(c.isComponent){b=c.getProxy().addCls(a)}else{b=c.createProxy({tag:"div",role:"presentation",cls:a,id:c.id+"-rzproxy"},Ext.getBody())}b.removeCls("x-proxy-el");return b},onStart:function(a){this.activeResizeHandle=Ext.get(this.getDragTarget().id);if(!this.dynamic){this.resize(this.startBox)}},onMouseDown:function(b,a){var c=Ext.fly(a.parentNode);Ext.dd.DragTracker.prototype.onMouseDown.apply(this,arguments);if(c&&c.shim){c.maskIframes()}},onMouseUp:function(a){var b=Ext.fly(this.dragTarget.parentNode);Ext.dd.DragTracker.prototype.onMouseUp.apply(this,arguments);if(b&&b.shim){b.unmaskIframes()}},onDrag:function(a){if(this.dynamic||this.proxy){this.updateDimensions(a)}},updateDimensions:function(q,m){var r=this,c=r.activeResizeHandle.region,g=r.getOffset(r.constrainTo?"dragTarget":null),k=r.startBox,h,o=0,s=0,j,p,a=0,u=0,t,i,b,d,n,l;c=r.convertRegionName(c);switch(c){case"south":s=g[1];b=2;break;case"north":s=-g[1];u=-s;b=2;break;case"east":o=g[0];b=1;break;case"west":o=-g[0];a=-o;b=1;break;case"northeast":s=-g[1];u=-s;o=g[0];i=[k.x,k.y+k.height];b=3;break;case"southeast":s=g[1];o=g[0];i=[k.x,k.y];b=3;break;case"southwest":o=-g[0];a=-o;s=g[1];i=[k.x+k.width,k.y];b=3;break;case"northwest":s=-g[1];u=-s;o=-g[0];a=-o;i=[k.x+k.width,k.y+k.height];b=3;break}d={width:k.width+o,height:k.height+s,x:k.x+a,y:k.y+u};j=Ext.Number.snap(d.width,r.widthIncrement);p=Ext.Number.snap(d.height,r.heightIncrement);if(j!==d.width||p!==d.height){switch(c){case"northeast":d.y-=p-d.height;break;case"north":d.y-=p-d.height;break;case"southwest":d.x-=j-d.width;break;case"west":d.x-=j-d.width;break;case"northwest":d.x-=j-d.width;d.y-=p-d.height}d.width=j;d.height=p}if(d.widthr.maxWidth){d.width=Ext.Number.constrain(d.width,r.minWidth,r.maxWidth);if(a){d.x=k.x+(k.width-d.width)}}else{r.lastX=d.x}if(d.heightr.maxHeight){d.height=Ext.Number.constrain(d.height,r.minHeight,r.maxHeight);if(u){d.y=k.y+(k.height-d.height)}}else{r.lastY=d.y}if(r.preserveRatio||q.shiftKey){h=r.startBox.width/r.startBox.height;n=Math.min(Math.max(r.minHeight,d.width/h),r.maxHeight);l=Math.min(Math.max(r.minWidth,d.height*h),r.maxWidth);if(b===1){d.height=n}else{if(b===2){d.width=l}else{t=Math.abs(i[0]-this.lastXY[0])/Math.abs(i[1]-this.lastXY[1]);if(t>h){d.height=n}else{d.width=l}if(c==="northeast"){d.y=k.y-(d.height-k.height)}else{if(c==="northwest"){d.y=k.y-(d.height-k.height);d.x=k.x-(d.width-k.width)}else{if(c==="southwest"){d.x=k.x-(d.width-k.width)}}}}}}r.setPosition=d.x!==r.startBox.x||d.y!==r.startBox.y;r.resize(d,m)},resize:function(d,a){var c=this,e,b=c.setPosition;if(c.dynamic||(!c.dynamic&&a)){if(b){c.target.setBox(d)}else{c.target.setSize(d.width,d.height)}}if(!a){e=c.getProxy();if(e&&e!==c.target){if(b||c.hideProxy){e.setBox(d)}else{e.setSize(d.width,d.height)}}}},onEnd:function(a){this.updateDimensions(a,true);if(this.proxy&&this.hideProxy){this.proxy.hide()}},convertRegionName:function(a){return a}},1,0,0,0,0,0,[Ext.resizer,"ResizeTracker"],0));(Ext.cmd.derive("Ext.resizer.Resizer",Ext.Base,{alternateClassName:"Ext.Resizable",handleCls:"x-resizable-handle",overCls:"x-resizable-handle-over",pinnedCls:"x-resizable-pinned",wrapCls:"x-resizable-wrap",wrappedCls:"x-resizable-wrapped",delimiterRe:/(?:\s*[,;]\s*)|\s+/,dynamic:true,handles:"s e se",height:null,width:null,heightIncrement:0,widthIncrement:0,minHeight:20,minWidth:20,maxHeight:10000,maxWidth:10000,pinned:false,preserveRatio:false,transparent:false,possiblePositions:{n:"north",s:"south",e:"east",w:"west",se:"southeast",sw:"southwest",nw:"northwest",ne:"northeast"},ariaRole:"presentation",constructor:function(b){var l=this,q=l.handles,k=Ext.dom.Element.unselectableCls,o=[],p,c,n,s,h,e,m,a,g,d,r,j;if(Ext.isString(b)||Ext.isElement(b)||b.dom){p=b;b=arguments[1]||{};b.target=p}l.mixins.observable.constructor.call(l,b);p=l.target;if(p){if(p.isComponent){p.addClsWithUI("resizable");if(p.minWidth){l.minWidth=p.minWidth}if(p.minHeight){l.minHeight=p.minHeight}if(p.maxWidth){l.maxWidth=p.maxWidth}if(p.maxHeight){l.maxHeight=p.maxHeight}if(p.floating){if(!l.hasOwnProperty("handles")){l.handles="n ne e se s sw w nw"}}l.el=p.getEl()}else{p=l.el=l.target=Ext.get(p)}}else{p=l.target=l.el=Ext.get(l.el)}l.el.addCls(Ext.Component.prototype.borderBoxCls);if(Ext.isNumber(l.width)){l.width=Ext.Number.constrain(l.width,l.minWidth,l.maxWidth)}if(Ext.isNumber(l.height)){l.height=Ext.Number.constrain(l.height,l.minHeight,l.maxHeight)}if(l.width!==null||l.height!==null){l.target.setSize(l.width,l.height)}s=l.el.dom.tagName.toUpperCase();if(s==="TEXTAREA"||s==="IMG"||s==="TABLE"){l.originalTarget=l.target;d=p.isComponent?p.getEl():p;l.el.addCls(l.wrappedCls);l.target=l.el=l.el.wrap({role:"presentation",cls:l.wrapCls,id:l.el.id+"-rzwrap",style:d.getStyle(["margin-top","margin-bottom"])});r=d.getPositioning();l.el.setPositioning(r);d.clearPositioning();g=d.getBox();if(r.position!=="absolute"){g.x=0;g.y=0}l.el.setBox(g);d.setStyle("position","absolute");l.isTargetWrapped=true}l.el.position();if(l.pinned){l.el.addCls(l.pinnedCls)}l.resizeTracker=new Ext.resizer.ResizeTracker({disabled:l.disabled,target:p,el:l.el,constrainTo:l.constrainTo,handleCls:l.handleCls,overCls:l.overCls,throttle:l.throttle,proxy:l.originalTarget?l.el:null,dynamic:l.originalTarget?true:l.dynamic,originalTarget:l.originalTarget,delegate:"."+l.handleCls,preserveRatio:l.preserveRatio,heightIncrement:l.heightIncrement,widthIncrement:l.widthIncrement,minHeight:l.minHeight,maxHeight:l.maxHeight,minWidth:l.minWidth,maxWidth:l.maxWidth});l.resizeTracker.on({mousedown:l.onBeforeResize,drag:l.onResize,dragend:l.onResizeEnd,scope:l});if(l.handles==="all"){l.handles="n s e w ne nw se sw"}q=l.handles=l.handles.split(l.delimiterRe);n=l.possiblePositions;h=q.length;c=l.handleCls+" "+l.handleCls+"-{0}";if(l.target.isComponent){j=l.target.baseCls;c+=" "+j+"-handle "+j+"-handle-{0}";if(Ext.supports.CSS3BorderRadius){c+=" "+j+"-handle-{0}-br"}}for(e=0;e")}}Ext.DomHelper.append(l.el,o.join(""));o.length=0;for(e=0;e-1){this.doSelect(a.record,false,b)}},onCellDeselect:function(a,b){if(a&&a.rowIdx!==undefined){this.doDeselect(a.record,b)}},onSelectChange:function(b,e,d,h){var g=this,i,c,a;if(e){i=g.nextSelection;c="select"}else{i=g.selection;c="deselect"}a=i.view||g.primaryView;if((d||g.fireEvent("before"+c,g,b,i.rowIdx,i.colIdx))!==false&&h()!==false){if(e){a.onCellSelect(i)}else{a.onCellDeselect(i);delete g.selection}if(!d){g.fireEvent(c,g,b,i.rowIdx,i.colIdx)}}},refresh:function(){var b=this.getPosition(),a;if(b&&(a=this.store.indexOf(this.selected.last()))!==-1){b.rowIdx=a}},onColumnMove:function(d,e,b,c){var a=d.up("tablepanel");if(a){this.onViewRefresh(a.view)}},onUpdate:function(a){var b=this,c;if(b.isSelected(a)){c=b.selecting?b.nextSelection:b.selection;b.view.onCellSelect(c)}},onViewRefresh:function(b){var e=this,h=e.getPosition(),c,g=b.headerCt,a,d;if(h&&h.view===b){a=h.record;d=h.column;if(!d.isDescendantOf(g)){d=g.queryById(d.id)||g.down('[text="'+d.text+'"]')||g.down('[dataIndex="'+d.dataIndex+'"]')}if(h.record){if(d&&(b.store.indexOfId(a.getId())!==-1)){c=new Ext.grid.CellContext(b).setPosition({row:a,column:d});e.setPosition(c)}}else{e.selection=null}}},selectByPosition:function(a,b){this.setPosition(a,b)}},0,0,0,0,["selection.cellmodel"],0,[Ext.selection,"CellModel"],0));(Ext.cmd.derive("Ext.selection.RowModel",Ext.selection.DataViewModel,{enableKeyNav:true,isRowModel:true,deselectOnContainerClick:false,onUpdate:function(b){var d=this,a=d.view,c;if(a&&d.isSelected(b)){c=a.indexOf(b);a.onRowSelect(c);if(b===d.lastFocused){a.onRowFocus(c,true)}}},onSelectChange:function(g,b,l,a){var j=this,m=j.views||[j.view],c=m.length,d=j.store.indexOf(g),h=b?"select":"deselect",e,k;if((l||j.fireEvent("before"+h,j,g,d))!==false&&a()!==false){for(e=0;e '},selectByPosition:function(a,b){if(!a.isCellContext){a=new Ext.grid.CellContext(this.view).setPosition(a.row,a.column)}if(!this.checkOnly||a.column===this.column){Ext.selection.RowModel.prototype.selectByPosition.call(this,a,b)}},onSelectChange:function(){Ext.selection.RowModel.prototype.onSelectChange.apply(this,arguments);if(!this.suspendChange){this.updateHeaderState()}},onStoreLoad:function(){Ext.selection.RowModel.prototype.onStoreLoad.apply(this,arguments);this.updateHeaderState()},onStoreAdd:function(){Ext.selection.RowModel.prototype.onStoreAdd.apply(this,arguments);this.updateHeaderState()},onStoreRemove:function(){Ext.selection.RowModel.prototype.onStoreRemove.apply(this,arguments);this.updateHeaderState()},onStoreRefresh:function(){Ext.selection.RowModel.prototype.onStoreRefresh.apply(this,arguments);this.updateHeaderState()},maybeFireSelectionChange:function(a){if(a&&!this.suspendChange){this.updateHeaderState()}Ext.selection.RowModel.prototype.maybeFireSelectionChange.apply(this,arguments)},resumeChanges:function(){Ext.selection.RowModel.prototype.resumeChanges.call(this);if(!this.suspendChange){this.updateHeaderState()}},updateHeaderState:function(){var g=this,h=g.store,e=h.getCount(),j=g.views,k=false,a=0,b,d,c;if(!h.isBufferedStore&&e>0){b=g.selected;k=true;for(c=0,d=b.getCount();c=a.value){g=a.value}}c.setValue(b,g,false);c.fireEvent("drag",c,h,d)}},getValueFromTracker:function(){var b=this.slider,a=this.tracker.getXY(),c;a[0]+=this.pointerOffset[0];a[1]+=this.pointerOffset[1];c=b.getTrackpoint(a);if(c!==undefined){return b.reversePixelValue(c)}},onDragEnd:function(d){var b=this,a=b.slider,c=b.value;a.onDragEnd(b,d);b.el.removeCls("x-slider-thumb-drag");b.dragging=a.dragging=false;a.fireEvent("dragend",a,d);if(b.dragStartValue!==c){a.fireEvent("changecomplete",a,c,b)}},destroy:function(){var a=this,b=this.anim;if(b){b.end()}a.el=a.tracker=a.anim=Ext.destroy(a.el,a.tracker);a.callParent()}},1,0,0,0,0,0,[Ext.slider,"Thumb"],0));(Ext.cmd.derive("Ext.slider.Tip",Ext.tip.Tip,{minWidth:10,offsets:null,align:null,position:"",defaultVerticalPosition:"left",defaultHorizontalPosition:"top",isSliderTip:true,init:function(c){var b=this,d,a;if(!b.position){b.position=c.vertical?b.defaultVerticalPosition:b.defaultHorizontalPosition}switch(b.position){case"top":a=[0,-10];d="b-t?";break;case"bottom":a=[0,10];d="t-b?";break;case"left":a=[-10,0];d="r-l?";break;case"right":a=[10,0];d="l-r?"}if(!b.align){b.align=d}if(!b.offsets){b.offsets=a}c.on({scope:b,dragstart:b.onSlide,drag:b.onSlide,dragend:b.hide,destroy:b.destroy})},onSlide:function(c,d,a){var b=this;b.show();b.update(b.getText(a));b.el.alignTo(a.el,b.align,b.offsets)},getText:function(a){return String(a.value)}},0,["slidertip"],["component","box","container","panel","tip","slidertip"],{component:true,box:true,container:true,panel:true,tip:true,slidertip:true},["widget.slidertip"],0,[Ext.slider,"Tip"],0));(Ext.cmd.derive("Ext.slider.Multi",Ext.form.field.Base,{alternateClassName:"Ext.slider.MultiSlider",vertical:false,minValue:0,maxValue:100,decimalPrecision:0,keyIncrement:1,pageSize:10,increment:0,clickRange:[5,15],clickToChange:true,animate:true,dragging:false,constrainThumbs:true,useTips:true,tipText:null,defaultBindProperty:"values",ariaRole:"slider",focusable:true,needArrowKeys:true,tabIndex:0,focusCls:"slider-focus",childEls:["endEl","innerEl"],fieldSubTpl:['
    tabindex="{tabIdx}"',' {$}="{.}"',">",'","
    ",{renderThumbs:function(g,e){var j=e.$comp,h=0,c=j.thumbs,b=c.length,d,a;for(;hg&&a.index>b.index){continue}b=a;l=h}}return b},onKeyDown:function(d){var c=this,a=c.ariaEl.dom,b,g;b=d.getKey();if(c.disabled||c.thumbs.length!==1){if(b!==d.TAB){d.preventDefault()}return}switch(b){case d.UP:case d.RIGHT:g=d.ctrlKey?c.maxValue:c.getValue(0)+c.keyIncrement;break;case d.DOWN:case d.LEFT:g=d.ctrlKey?c.minValue:c.getValue(0)-c.keyIncrement;break;case d.HOME:g=c.minValue;break;case d.END:g=c.maxValue;break;case d.PAGE_UP:g=c.getValue(0)+c.pageSize;break;case d.PAGE_DOWN:g=c.getValue(0)-c.pageSize;break}if(g!==undefined){d.stopEvent();g=c.normalizeValue(g);c.setValue(0,g,undefined,true);if(a){a.setAttribute("aria-valuenow",g)}}},normalizeValue:function(b){var c=this,a=c.zeroBasedSnapping?"snap":"snapInRange";b=Ext.Number[a](b,c.increment,c.minValue,c.maxValue);b=Ext.util.Format.round(b,c.decimalPrecision);b=Ext.Number.constrain(b,c.minValue,c.maxValue);return b},setMinValue:function(h){var g=this,c=g.thumbs,b=c.length,a=g.ariaEl.dom,d,e;g.minValue=h;for(e=0;eh){g.setValue(e,h,false)}}if(a){a.setAttribute("aria-valuemax",h)}g.syncThumbs()},setValue:function(g,l,b,d){var k=this,j=k.thumbs,h=k.ariaEl.dom,a,e,c,m;if(Ext.isArray(g)){m=g;b=l;for(c=0,e=m.length;c'].join("")},_drawCircle:function(d,i,g,c,h,a,b){var e=c*2,k,j;i-=c;g-=c;k=h==null?' stroked="false" ':' strokeWeight="'+b+'px" strokeColor="'+h+'" ';j=a==null?' filled="false"':' fillColor="'+a+'" filled="true" ';return[''].join("")},_drawPieSlice:function(j,o,m,i,k,e,n,d){var l,c=this.pixelWidth,p=this.pixelHeight,b,a,h,g,r=n==null?' stroked="false" ':' strokeWeight="1px" strokeColor="'+n+'" ',q=d==null?' filled="false"':' fillColor="'+d+'" filled="true" ';if(k===e){return""}if((e-k)===(2*Math.PI)){k=0;e=(2*Math.PI)}b=o+Math.round(Math.cos(k)*i);a=m+Math.round(Math.sin(k)*i);h=o+Math.round(Math.cos(e)*i);g=m+Math.round(Math.sin(e)*i);if(b===h&&a===g){if((e-k)'].join("")},_drawRect:function(e,b,h,c,a,d,g){return this._drawShape(e,[[b,h],[b,h+a],[b+c,h+a],[b+c,h],[b,h]],d,g)},reset:function(){Ext.fly(this.group).empty()},appendShape:function(a){this.prerender.push(this["_draw"+a.type].apply(this,a.args));this.lastShapeId=a.id;return a.id},replaceWithShape:function(d,a){var c=this.el.getById("jqsshape"+d,true),b=this["_draw"+a.type].apply(this,a.args);c.outerHTML=b},replaceWithShapes:function(b,a){var e=this.el.getById("jqsshape"+b[0],true),d="",g=a.length,c;for(c=0;c")]}return""},getRegionFields:Ext.emptyFn,calcHighlightColor:function(a){var d=this,j=d.getHighlightColor(),c=d.getHighlightLighten(),h,g,e,b;if(j){return j}if(c){h=/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(a)||/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(a);if(h){e=[];g=a.length===4?16:1;for(b=0;b<3;b++){e[b]=Ext.Number.constrain(Math.round(parseInt(h[b+1],16)*g*c),0,255)}return"rgb("+e.join(",")+")"}}return a},destroy:function(){delete this.redrawQueue[this.getId()];Ext.Widget.prototype.destroy.call(this)}},1,0,["widget"],{widget:true},0,0,[Ext.sparkline,"Base"],function(b){var a=b.prototype;Ext.onInternalReady(function(){a.tooltip=new Ext.tip.ToolTip({id:"sparklines-tooltip",showDelay:0,dismissDelay:0,hideDelay:400})});b.onClassCreated(b);a.processRedrawQueue=function(){var c=a.redrawQueue,d;for(d in c){c[d].redraw()}a.redrawQueue={};a.redrawTimer=0};if(!Ext.supports.Canvas){b.prototype.element={tag:"span",reference:"element",listeners:{mouseenter:"onMouseEnter",mouseleave:"onMouseLeave",mousemove:"onMouseMove"},style:{display:"inline-block",position:"relative",overflow:"hidden",margin:"0px",padding:"0px",verticalAlign:"top",cursor:"default"},children:[{tag:"svml:group",reference:"groupEl",coordorigin:"0 0",coordsize:"0 0",style:"position:absolute;width:0;height:0;pointer-events:none"}]}}}));(Ext.cmd.derive("Ext.sparkline.BarBase",Ext.sparkline.Base,{renderHighlight:function(a){this.renderRegion(a,true)},renderGraph:function(){var h=this,b=h.values,d=h.canvas,k=h.regionShapes||(h.regionShapes={}),a,g,e,c;if(!Ext.sparkline.Base.prototype.renderGraph.call(this)){return}for(e=b.length;e--;){a=h.renderRegion(e);if(a){if(Ext.isArray(a)){g=[];for(c=a.length;c--;){a[c].append();g.push(a[c].id)}k[e]=g}else{a.append();k[e]=a.id}}else{k[e]=null}}if(h.currentPageXY){h.currentRegion=null;h.updateDisplay()}d.render()}},0,0,["widget"],{widget:true},0,0,[Ext.sparkline,"BarBase"],0));(Ext.cmd.derive("Ext.sparkline.RangeMap",Ext.Base,{constructor:function(d){var c,a,b=[];for(c in d){if(d.hasOwnProperty(c)&&typeof c==="string"&&c.indexOf(":")>-1){a=c.split(":");a[0]=a[0].length===0?-Infinity:parseFloat(a[0]);a[1]=a[1].length===0?Infinity:parseFloat(a[1]);a[2]=d[c];b.push(a)}}this.map=d;this.rangelist=b||false},get:function(e){var d=this.rangelist,c,b,a;if((a=this.map[e])!==undefined){return a}if(d){for(c=d.length;c--;){b=d[c];if(b[0]<=e&&b[1]>=e){return b[2]}}}}},1,0,0,0,0,0,[Ext.sparkline,"RangeMap"],0));(Ext.cmd.derive("Ext.sparkline.Bar",Ext.sparkline.BarBase,{config:{barColor:"#3366cc",negBarColor:"#f44",stackedBarColor:["#3366cc","#dc3912","#ff9900","#109618","#66aa00","#dd4477","#0099c6","#990099"],zeroColor:null,nullColor:null,zeroAxis:true,barWidth:4,barSpacing:1,chartRangeMin:null,chartRangeMax:null,chartRangeClip:false,colorMap:null,tipTpl:new Ext.XTemplate("● {prefix}{value}{suffix}")},remove:function(e,c){var b,d,a=[];for(b=0,d=e.length;b-1;if(v||Ext.isArray(J)){E=true;if(v){J=c[B]=I.normalizeValues(J.split(":"))}J=I.remove(J,null);r=Math.min.apply(Math,J);u=Math.max.apply(Math,J);if(rG){G=u}}}I.stacked=E;I.regionShapes={};I.totalBarWidth=t+q;I.width=(c.length*t)+((c.length-1)*q);if(m){C=k==null?-Infinity:k;F=l==null?Infinity:l}g=[];e=E?[]:g;for(B=0,h=c.length;B0){b[B]+=J}if(D<0&&G>0){if(J<0){a[B]+=Math.abs(J)}else{e[B]+=J}}else{e[B]+=Math.abs(J-(J<0?G:D))}g.push(J)}}}else{J=m?Ext.Number.constrain(c[B],C,F):c[B];J=c[B]=I.normalizeValue(J);if(J!==null){g.push(J)}}}I.max=A=Math.max.apply(Math,g);I.min=x=Math.min.apply(Math,g);I.stackMax=G=E?Math.max.apply(Math,b):A;I.stackMin=D=E?Math.min.apply(Math,g):x;if(k!=null&&(m||kA)){A=l}if(x<=0&&A>=0&&z){o=0}else{if(!z){o=x}else{if(x>0){o=x}else{o=A}}}I.xAxisOffset=o;s=E?(Math.max.apply(Math,e)+Math.max.apply(Math,a)):A-x;I.canvasHeightEf=(z&&x<0)?I.getHeight()-2:I.getHeight()-1;if(x=0)?G:A;n=(d-o)/s*I.getHeight();if(n!==Math.ceil(n)){I.canvasHeightEf-=2;n=Math.ceil(n)}}else{n=I.getHeight()}I.yoffset=n;I.range=s},getRegion:function(b,c){var a=Math.floor(b/this.totalBarWidth);return(a<0||a>=this.values.length)?undefined:a},getRegionFields:function(e){var b=Ext.Array.from(this.values[e]),a=[],d,c;for(c=b.length;c--;){d=b[c];a.push({isNull:d===null,value:d,color:this.calcColor(c,d,e),offset:e})}return a},calcColor:function(j,i,d){var g=this,b=g.colorMapByIndex,h=g.colorMapByValue,a,e,c=g.getZeroColor();if(this.stacked){a=g.getStackedBarColor()}else{a=(i<0)?g.getNegBarColor():g.getBarColor()}if(i===0&&c!==undefined){a=c}if(h&&(e=h.get(i))){a=e}else{if(b&&b.length>d){a=b[d]}}return Ext.isArray(a)?a[j%a.length]:a},renderRegion:function(l,e){var z=this,n=z.values[l],a=z.xAxisOffset,o=z.range,w=z.stacked,c=z.canvas,q=z.getBarWidth(),j=l*z.totalBarWidth,b=z.canvasHeightEf,k=z.yoffset,h,r,t,s,g,u,d,A,v,m,p=z.getNullColor();n=Ext.isArray(n)?n:[n];d=n.length;A=n[0];s=z.all(n,null);m=z.all(n,a,true);if(s){if(p){t=e?p:z.calcHighlightColor(p,z);h=(k>0)?k-1:k;c.drawRect(j,h,q-1,0,t,t).append()}return}g=k;for(u=0;u0){r=Math.floor(b*((Math.abs(A-a)/o)))+1}else{r=1}if(A5){B=h[0];w=h[1];s=h[2];r=h[3];p=h[4];b=h[5];m=h[6]}else{w=h[0];s=h[1];r=h[2];p=h[3];b=h[4]}}else{h.sort(function(G,i){return G-i});s=E.quartile(h,1);r=E.quartile(h,2);p=E.quartile(h,3);j=p-s;if(v){w=b=null;for(A=0;As-(j*u)){w=h[A]}if(h[A]b){l.drawCircle((m-x)*g+z,e/2,F,a,k).append()}}l.drawRect(Math.round((s-x)*g+z),Math.round(e*0.1),Math.round((p-s)*g),Math.round(e*0.8),E.getBoxLineColor(),E.getBoxFillColor()).append();l.drawLine(Math.round((w-x)*g+z),Math.round(e/2),Math.round((s-x)*g+z),Math.round(e/2),d).append();l.drawLine(Math.round((w-x)*g+z),Math.round(e/4),Math.round((w-x)*g+z),Math.round(e-e/4),c).append();l.drawLine(Math.round((b-x)*g+z),Math.round(e/2),Math.round((p-x)*g+z),Math.round(e/2),d).append();l.drawLine(Math.round((b-x)*g+z),Math.round(e/4),Math.round((b-x)*g+z),Math.round(e-e/4),c).append();l.drawLine(Math.round((r-x)*g+z),Math.round(e*0.1),Math.round((r-x)*g+z),Math.round(e*0.9),E.getMedianColor()).append();if(E.target){y=Math.ceil(E.spotRadius);l.drawLine(Math.round((E.target-x)*g+z),Math.round((e/2)-y),Math.round((E.target-x)*g+z),Math.round((e/2)+y),n).append();l.drawLine(Math.round((E.target-x)*g+z-y),Math.round(e/2),Math.round((E.target-x)*g+z+y),Math.round(e/2),n).append()}if(E.currentPageXY&&E.el.getRegion().contains(E.currentPageXY)){E.currentRegion=null;E.updateDisplay()}l.render()}},0,["sparklinebox"],["widget","sparklinebox"],{widget:true,sparklinebox:true},["widget.sparklinebox"],0,[Ext.sparkline,"Box"],0));(Ext.cmd.derive("Ext.sparkline.Bullet",Ext.sparkline.Base,{config:{targetColor:"#f33",targetWidth:3,performanceColor:"#33f",rangeColors:["#d3dafe","#a8b6ff","#7f94ff"],base:null,tipTpl:new Ext.XTemplate("{fieldkey:this.fields} - {value}",{fields:function(a){if(a==="r"){return"Range"}if(a==="p"){return"Performance"}if(a==="t"){return"Target"}}})},applyValues:function(a){a=Ext.Array.map(Ext.Array.from(a),this.normalizeValue);this.disabled=!(a&&a.length);return a},onUpdate:function(){var d=this,b=d.values,c,a,g,e=d.getBase();Ext.sparkline.Base.prototype.onUpdate.apply(this,arguments);g=b.slice();g[0]=g[0]===null?g[2]:g[0];g[1]=b[1]===null?g[2]:g[1];c=Math.min.apply(Math,b);a=Math.max.apply(Math,b);if(e==null){c=c<0?c:0}else{c=e}d.min=c;d.max=a;d.range=a-c;d.shapes={};d.valueShapes={};d.regiondata={};if(!b.length){d.disabled=true}},getRegion:function(a,c){var b=this.canvas.getShapeAt(a,c);return(b!==undefined&&this.shapes[b]!==undefined)?this.shapes[b]:undefined},getRegionFields:function(a){return{fieldkey:a.substr(0,1),value:this.values[a.substr(1)],region:a}},renderHighlight:function(a){switch(a.substr(0,1)){case"r":this.renderRange(a.substr(1),true).append();break;case"p":this.renderPerformance(true).append();break;case"t":this.renderTarget(true).append();break}},renderRange:function(e,b){var d=this.values[e],c=Math.round(this.getWidth()*((d-this.min)/this.range)),a=this.getRangeColors()[e-2];if(b){a=this.calcHighlightColor(a)}return this.canvas.drawRect(0,0,c-1,this.getHeight()-1,a,a)},renderPerformance:function(b){var d=this.values[1],c=Math.round(this.getWidth()*((d-this.min)/this.range)),a=this.getPerformanceColor();if(b){a=this.calcHighlightColor(a)}return this.canvas.drawRect(0,Math.round(this.getHeight()*0.3),c-1,Math.round(this.getHeight()*0.4)-1,a,a)},renderTarget:function(c){var h=this.values[0],e=this.getTargetWidth(),a=Math.round(this.getWidth()*((h-this.min)/this.range)-(e/2)),g=Math.round(this.getHeight()*0.1),d=this.getHeight()-(g*2),b=this.getTargetColor();if(c){b=this.calcHighlightColor(b)}return this.canvas.drawRect(a,g,e-1,d-1,b,b)},renderGraph:function(){var g=this,h=g.values.length,d=g.canvas,e,c,b=g.shapes||(g.shapes={}),a=g.valueShapes||(g.valueShapes={});if(!Ext.sparkline.Base.prototype.renderGraph.call(this)){return}for(e=2;ec.max)){c.max=d}if(c.canvas){if(c.getLineHeight()==="auto"){c.setLineHeight(Math.round(c.getHeight()*0.3))}}},getRegion:function(a,b){return Math.floor(a/this.itemWidth)},getRegionFields:function(a){return{isNull:this.values[a]===undefined,value:this.values[a],offset:a}},renderRegion:function(k,e){var m=this,r=m.values,h=m.min,n=m.max,j=m.range,c=m.interval,b=m.canvas,i=m.getHeight(),q=m.getLineHeight(),l=i-q,a,d,g,p,o=m.getThresholdColor();d=Ext.Number.constrain(r[k],h,n);p=k*c;a=Math.round(l-l*((d-h)/j));g=(o&&d=c[b][0]&&a<=c[b][1]){return c[b][2]}}return undefined},getRegionFields:function(a){return{isNull:this.yvalues[a]===null,x:this.xvalues[a],y:this.yvalues[a],color:this.getLineColor(),fillColor:this.getFillColor(),offset:a}},renderHighlight:function(g){var c=this,a=c.canvas,e=c.vertices[g],b=c.getSpotRadius(),h=c.getHighlightSpotColor(),d=c.getHighlightLineColor();if(!e){return}if(b&&h){a.drawCircle(e[0],e[1],b,null,h).append()}if(d){a.drawLine(e[0],c.canvasTop,e[0],c.canvasTop+c.getHeight(),d).append()}},scanValues:function(){var k=this,l=k.values,c=l.length,a=k.xvalues,h=k.yvalues,m=k.yminmax,e,d,j,g,b;for(e=0;ed.maxy){d.maxy=g}}if(a!=null&&(d.chartRangeClip||ad.maxy)){this.maxy=e}if(b!=null&&(d.chartRangeClipX||bd.maxx)){d.maxx=h}},drawNormalRange:function(c,h,g,d,i){var a=this.getNormalRangeMin(),e=this.getNormalRangeMax(),b=h+Math.round(g-(g*((e-this.miny)/i))),j=Math.round((g*(e-a))/i);this.canvas.drawRect(c,b,d,j,undefined,this.normalRangeColor).append()},renderGraph:function(){var q=this,k=q.canvas,M=q.getWidth(),m=q.getHeight(),d=q.vertices,L=q.getSpotRadius(),O=q.regionMap,B,g,C,t,r,s,J,A,F,E,j,w,n,p,h,K,e,D,o,c=q.getValueSpots(),H,u,G,I,N,l=q.getSpotColor(),b=q.getMinSpotColor(),z=q.getMaxSpotColor(),v=q.getNormalRangeMin(),a=q.getDrawNormalOnTop();if(!Ext.sparkline.Base.prototype.renderGraph.call(this)){return}q.scanValues();q.processRangeOptions();G=q.xvalues;I=q.yvalues;if(!q.yminmax.length||q.yvalues.length<2){return}t=r=0;B=q.maxx-q.minx===0?1:q.maxx-q.minx;g=q.maxy-q.miny===0?1:q.maxy-q.miny;C=q.yvalues.length-1;if(L&&(M<(L*4)||m<(L*4))){L=0}if(L){H=q.getHighlightSpotColor()&&!q.disableInteraction;if(H||b||(l&&I[C]===q.miny)){m-=Math.ceil(L)}if(H||z||(l&&I[C]===q.maxy)){m-=Math.ceil(L);t+=Math.ceil(L)}if(H||((b||z)&&(I[0]===q.miny||I[0]===q.maxy))){r+=Math.ceil(L);M-=Math.ceil(L)}if(H||l||(b||z&&(I[C]===q.miny||I[C]===q.maxy))){M-=Math.ceil(L)}}m--;if(v!=null&&!a){q.drawNormalRange(r,t,m,M,g)}J=[];A=[J];p=h=null;K=I.length;for(N=0;Nq.maxy){E=q.maxy}if(!J.length){J.push([w,t+m])}s=[w,t+Math.round(m-(m*((E-this.miny)/g)))];J.push(s);d.push(s)}}e=[];D=[];o=A.length;for(N=0;N2){J[0]=[J[0][0],J[1][1]]}e.push(J)}}o=D.length;for(N=0;N0){for(b=a.length;b--;){c+=a[b]}}d.total=c;d.radius=Math.floor(Math.min(d.getWidth(),d.getHeight())/2)},getRegion:function(a,d){var b=window.devicePixelRatio||1,c=this.canvas.getShapeAt(a*b,d*b);return(c!=null&&this.shapes[c]!=null)?this.shapes[c]:null},getRegionFields:function(b){var a=this.getSliceColors();return{isNull:this.values[b]==null,value:this.values[b],percent:this.values[b]/this.total*100,color:a[b%a.length],offset:b}},renderHighlight:function(a){this.renderSlice(a,true).append()},renderSlice:function(m,e){var o=this,d=o.canvas,n=o.radius,a=o.getBorderWidth(),j=o.getOffset(),b=2*Math.PI,s=o.values,p=o.total,l=j?(2*Math.PI)*(j/360):0,c,h,k,q,g,r=this.getSliceColors();q=s.length;for(k=0;k0){h=l+(b*(s[k]/p))}if(m===k){g=r[k%r.length];if(e){g=o.calcHighlightColor(g)}return d.drawPieSlice(n,n,n-a,c,h,null,g)}l=h}},renderGraph:function(){var h=this,c=h.canvas,k=h.values,e=h.radius,a=h.getBorderWidth(),g,d,b=h.shapes||(h.shapes={}),j=h.valueShapes||(h.valueShapes={});if(!Ext.sparkline.Base.prototype.renderGraph.call(this)){return}if(a){c.drawCircle(e,e,Math.floor(e-(a/2)),h.getBorderColor(),null,a).append()}for(d=k.length;d--;){if(k[d]){g=h.renderSlice(d).append();j[d]=g.id;b[g.id]=d}}if(h.currentPageXY&&h.el.getRegion().contains(h.currentPageXY)){h.currentRegion=null;h.updateDisplay()}c.render()}},0,["sparklinepie"],["widget","sparklinepie"],{widget:true,sparklinepie:true},["widget.sparklinepie"],0,[Ext.sparkline,"Pie"],0));(Ext.cmd.derive("Ext.sparkline.TriState",Ext.sparkline.BarBase,{config:{barWidth:4,barSpacing:1,posBarColor:"#6f6",negBarColor:"#f44",zeroBarColor:"#999",colorMap:{},tipTpl:new Ext.XTemplate("● {value:this.states}",{states:function(a){var b=Number(a);if(b===-1){return"Loss"}if(b===0){return"Draw"}if(b===1){return"Win"}return a}})},applyColorMap:function(a){var b=this;if(Ext.isArray(a)){b.colorMapByIndex=a;b.colorMapByValue=null}else{b.colorMapByIndex=null;b.colorMapByValue=a;if(b.colorMapByValue&&b.colorMapByValue.get==null){b.colorMapByValue=new Ext.sparkline.RangeMap(a)}}return a},applyValues:function(a){a=Ext.Array.map(Ext.Array.from(a),Number);this.disabled=!(a&&a.length);return a},onUpdate:function(){this.totalBarWidth=this.getBarWidth()+this.getBarSpacing()},getBarWidth:function(){var a=this.values;return this._barWidth||(this.getWidth()-(a.length-1)*this.getBarSpacing())/a.length},getRegion:function(a,b){return Math.floor(a/this.totalBarWidth)},getRegionFields:function(a){return{isNull:this.values[a]==null,value:this.values[a],color:this.calcColor(this.values[a],a),offset:a}},calcColor:function(g,h){var e=this,b=e.values,i=e.colorMapByIndex,d=e.colorMapByValue,a,c;if(d&&(c=d.get(g))){a=c}else{if(i&&i.length>h){a=i[h]}else{if(b[h]<0){a=e.getNegBarColor()}else{if(b[h]>0){a=e.getPosBarColor()}else{a=e.getZeroBarColor()}}}}return a},renderRegion:function(e,b){var h=this,k=h.values,a=h.canvas,d,l,g,j,i,c;d=a.pixelHeight;g=Math.round(d/2);j=e*h.totalBarWidth;if(k[e]<0){i=g;l=g-1}else{if(k[e]>0){i=0;l=g-1}else{i=g-1;l=2}}c=h.calcColor(k[e],e);if(c==null){return}if(b){c=h.calcHighlightColor(c)}a.drawRect(j,i,h.getBarWidth()-1,l-1,c,c).append()}},0,["sparklinetristate"],["widget","sparklinetristate"],{widget:true,sparklinetristate:true},["widget.sparklinetristate"],0,[Ext.sparkline,"TriState"],0));(Ext.cmd.derive("Ext.state.CookieProvider",Ext.state.Provider,{constructor:function(a){var b=this;b.path="/";b.expires=new Date(Ext.Date.now()+(1000*60*60*24*7));b.domain=null;b.secure=false;Ext.state.Provider.prototype.constructor.apply(this,arguments);b.state=b.readCookies()},set:function(a,c){var b=this;if(typeof c==="undefined"||c===null){b.clear(a);return}b.setCookie(a,c);Ext.state.Provider.prototype.set.apply(this,arguments)},clear:function(a){this.clearCookie(a);Ext.state.Provider.prototype.clear.apply(this,arguments)},readCookies:function(){var e={},j=document.cookie+";",d=/\s?(.*?)=(.*?);/g,i=this.prefix,a=i.length,h,b,g;while((h=d.exec(j))!=null){b=h[1];g=h[2];if(b&&b.substring(0,a)===i){e[b.substr(a)]=this.decodeValue(g)}}return e},setCookie:function(a,c){var b=this;document.cookie=b.prefix+a+"="+b.encodeValue(c)+((b.expires==null)?"":("; expires="+b.expires.toUTCString()))+((b.path==null)?"":("; path="+b.path))+((b.domain==null)?"":("; domain="+b.domain))+(b.secure?"; secure":"")},clearCookie:function(a){var b=this;document.cookie=b.prefix+a+"=null; expires=Thu, 01-Jan-1970 00:00:01 GMT"+((b.path==null)?"":("; path="+b.path))+((b.domain==null)?"":("; domain="+b.domain))+(b.secure?"; secure":"")}},1,0,0,0,0,0,[Ext.state,"CookieProvider"],0));(Ext.cmd.derive("Ext.state.LocalStorageProvider",Ext.state.Provider,{constructor:function(){var a=this;Ext.state.Provider.prototype.constructor.apply(this,arguments);a.store=a.getStorageObject();if(a.store){a.state=a.readLocalStorage()}else{a.state={}}},readLocalStorage:function(){var a=this.store,e={},d=a.getKeys(),c=d.length,b;while(c--){b=d[c];e[b]=this.decodeValue(a.getItem(b))}return e},set:function(a,c){var b=this;b.clear(a);if(c!=null){b.store.setItem(a,b.encodeValue(c));Ext.state.Provider.prototype.set.apply(this,arguments)}},clear:function(a){this.store.removeItem(a);Ext.state.Provider.prototype.clear.apply(this,arguments)},getStorageObject:function(){var a=this.prefix,c=a,b=c.length-1;if(c.charAt(b)==="-"){c=c.substring(0,b)}return new Ext.util.LocalStorage({id:c,prefix:a})}},1,0,0,0,["state.localstorage"],0,[Ext.state,"LocalStorageProvider"],0));(Ext.cmd.derive("Ext.tab.Tab",Ext.button.Button,{isTab:true,baseCls:"x-tab",closeElOverCls:"x-tab-close-btn-over",closeElPressedCls:"x-tab-close-btn-pressed",config:{rotation:"default",tabPosition:"top"},closable:true,closeText:"Close Tab",active:false,childEls:["closeEl"],scale:false,ariaRole:"tab",tabIndex:-1,keyHandlers:{DELETE:"onDeleteKey"},_btnWrapCls:"x-tab-wrap",_btnCls:"x-tab-button",_baseIconCls:"x-tab-icon-el",_glyphCls:"x-tab-glyph",_innerCls:"x-tab-inner",_textCls:"x-tab-text",_noTextCls:"x-tab-no-text",_hasIconCls:"x-tab-icon",_activeCls:"x-tab-active",_closableCls:"x-tab-closable",overCls:"x-tab-over",_pressedCls:"x-tab-pressed",_disabledCls:"x-tab-disabled",_rotateClasses:{1:"x-tab-rotate-right",2:"x-tab-rotate-left"},_positions:{top:{"default":"top",0:"top",1:"left",2:"right"},right:{"default":"top",0:"right",1:"top",2:"bottom"},bottom:{"default":"bottom",0:"bottom",1:"right",2:"left"},left:{"default":"top",0:"left",1:"bottom",2:"top"}},_defaultRotations:{top:0,right:1,bottom:0,left:2},initComponent:function(){var a=this;if(a.card){a.setCard(a.card)}Ext.button.Button.prototype.initComponent.apply(this,arguments)},getActualRotation:function(){var a=this.getRotation();return(a!=="default")?a:this._defaultRotations[this.getTabPosition()]},updateRotation:function(){this.syncRotationAndPosition()},updateTabPosition:function(){this.syncRotationAndPosition()},syncRotationAndPosition:function(){var h=this,c=h._rotateClasses,b=h.getTabPosition(),g=h.getActualRotation(),e=h._rotateCls,i=h._rotateCls=c[g],d=h._positionCls,a=h._positionCls=h._positions[b][g];if(e!==i){if(e){h.removeCls(e)}if(i){h.addCls(i)}}if(d!==a){if(d){h.removeClsWithUI(d)}if(a){h.addClsWithUI(a)}if(h.rendered){h.updateFrame()}}if(h.rendered){h.setElOrientation()}},onAdded:function(b,c,a){Ext.button.Button.prototype.onAdded.call(this,b,c,a);this.syncRotationAndPosition()},getTemplateArgs:function(){var b=this,a=Ext.button.Button.prototype.getTemplateArgs.call(this);a.closable=b.closable;a.closeText=b.closeText;return a},beforeRender:function(){var b=this,a=b.up("tabbar"),c=b.up("tabpanel");Ext.button.Button.prototype.beforeRender.call(this);b.ariaRenderAttributes=b.ariaRenderAttributes||{};if(b.active){b.ariaRenderAttributes["aria-selected"]=true;b.addCls(b._activeCls)}else{b.ariaRenderAttributes["aria-selected"]=false}b.syncClosableCls();if(!b.minWidth){b.minWidth=(a)?a.minTabWidth:b.minWidth;if(!b.minWidth&&c){b.minWidth=c.minTabWidth}if(b.minWidth&&b.iconCls){b.minWidth+=25}}if(!b.maxWidth){b.maxWidth=(a)?a.maxTabWidth:b.maxWidth;if(!b.maxWidth&&c){b.maxWidth=c.maxTabWidth}}},onRender:function(){var a=this;a.setElOrientation();Ext.button.Button.prototype.onRender.apply(this,arguments);if(a.closable){a.closeEl.addClsOnOver(a.closeElOverCls);a.closeEl.addClsOnClick(a.closeElPressedCls)}},setElOrientation:function(){var c=this,a=c.getActualRotation(),b=c.el;if(a){b.setVertical(a===1?90:270)}else{b.setHorizontal()}},enable:function(a){var b=this;Ext.button.Button.prototype.enable.apply(this,arguments);b.removeCls(b._disabledCls);return b},disable:function(a){var b=this;Ext.button.Button.prototype.disable.apply(this,arguments);b.addCls(b._disabledCls);return b},setClosable:function(a){var b=this;a=(!arguments.length||!!a);if(b.closable!==a){b.closable=a;if(b.card){b.card.closable=a}b.syncClosableCls();if(b.rendered){b.syncClosableElements();b.updateLayout()}}},syncClosableElements:function(){var a=this,b=a.closeEl;if(a.closable){if(!b){b=a.closeEl=a.btnWrap.insertSibling({tag:"span",id:a.id+"-closeEl",cls:a.baseCls+"-close-btn",html:a.closeText},"after")}b.addClsOnOver(a.closeElOverCls);b.addClsOnClick(a.closeElPressedCls)}else{if(b){b.destroy();delete a.closeEl}}},syncClosableCls:function(){var b=this,a=b._closableCls;if(b.closable){b.addCls(a)}else{b.removeCls(a)}},setCard:function(a){var b=this;b.card=a;if(a.iconAlign){b.setIconAlign(a.iconAlign)}if(a.textAlign){b.setTextAlign(a.textAlign)}b.setText(b.title||a.title);b.setIconCls(b.iconCls||a.iconCls);b.setIcon(b.icon||a.icon);b.setGlyph(b.glyph||a.glyph)},onCloseClick:function(){var a=this;if(a.fireEvent("beforeclose",a)!==false){if(a.tabBar){if(a.tabBar.closeTab(a)===false){return}}else{a.fireClose()}}},fireClose:function(){this.fireEvent("close",this)},onEnterKey:function(b){var a=this;if(a.tabBar){a.tabBar.onClick(b,a.el);b.stopEvent();return false}},onDeleteKey:function(a){if(this.closable){this.onCloseClick();a.stopEvent();return false}},beforeClick:function(a){if(!a){this.focus()}},activate:function(d){var c=this,b=c.card,a=c.ariaEl.dom;c.active=true;c.addCls(c._activeCls);if(a){a.setAttribute("aria-selected",true)}else{c.ariaRenderAttributes=c.ariaRenderAttributes||{};c.ariaRenderAttributes["aria-selected"]=true}if(b){if(b.ariaEl.dom){b.ariaEl.dom.setAttribute("aria-expanded",true)}else{b.ariaRenderAttributes=b.ariaRenderAttributes||{};b.ariaRenderAttributes["aria-expanded"]=true}}if(d!==true){c.fireEvent("activate",c)}},deactivate:function(d){var c=this,b=c.card,a=c.ariaEl.dom;c.active=false;c.removeCls(c._activeCls);if(a){a.setAttribute("aria-selected",false)}else{c.ariaRenderAttributes=c.ariaRenderAttributes||{};c.ariaRenderAttributes["aria-selected"]=false}if(b){if(b.ariaEl.dom){b.ariaEl.dom.setAttribute("aria-expanded",false)}else{b.ariaRenderAttributes=b.ariaRenderAttributes||{};b.ariaRenderAttributes["aria-expanded"]=false}}if(d!==true){c.fireEvent("deactivate",c)}},privates:{getFramingInfoCls:function(){return this.baseCls+"-"+this.ui+"-"+this._positionCls},wrapPrimaryEl:function(a){Ext.Button.superclass.wrapPrimaryEl.call(this,a)}}},0,["tab"],["component","box","button","tab"],{component:true,box:true,button:true,tab:true},["widget.tab"],0,[Ext.tab,"Tab"],0));(Ext.cmd.derive("Ext.tab.Bar",Ext.panel.Bar,{baseCls:"x-tab-bar",componentLayout:"body",isTabBar:true,config:{tabRotation:"default",tabStretchMax:true,activateOnFocus:true},defaultType:"tab",plain:false,ensureActiveVisibleOnChange:true,ariaRole:"tablist",childEls:["body","strip"],_stripCls:"x-tab-bar-strip",_baseBodyCls:"x-tab-bar-body",renderTpl:'',_reverseDockNames:{left:"right",right:"left"},_layoutAlign:{top:"end",right:"begin",bottom:"begin",left:"end"},initComponent:function(){var d=this,a=d.initialConfig.layout,c=a&&a.align,b=a&&a.overflowHandler;if(d.plain){d.addCls(d.baseCls+"-plain")}Ext.panel.Bar.prototype.initComponent.call(this);d.setLayout({align:c||(d.getTabStretchMax()?"stretchmax":d._layoutAlign[d.dock]),overflowHandler:b||"scroller"});d.on({click:d.onClick,element:"el",scope:d})},ensureTabVisible:function(b){var c=this,d=c.tabPanel,a=c.layout.overflowHandler;if(c.rendered&&a&&c.tooNarrow&&a.scrollToItem){if(b||b===0){if(!b.isTab){if(Ext.isNumber(b)){b=this.items.getAt(b)}else{if(b.isComponent&&d&&d.items.contains(b)){b=b.tab}}}}if(!b){b=c.activeTab}if(b){a.scrollToItem(b)}}},initRenderData:function(){var a=this;return Ext.apply(Ext.panel.Bar.prototype.initRenderData.call(this),{bodyCls:a.bodyCls,baseBodyCls:a._baseBodyCls,bodyTargetCls:a.bodyTargetCls,stripCls:a._stripCls,dock:a.dock})},setDock:function(h){var g=this,a=g.items,c=g.ownerCt,e,b,d;a=a&&a.items;if(a){for(b=0,d=a.length;b1){if(d&&d!==b&&!d.disabled){a=d}else{a=b.next("tab[disabled=false]")||b.prev("tab[disabled=false]")}}return a||c.activeTab},setActiveTab:function(b,a){var c=this;if(!b.disabled&&b!==c.activeTab){if(c.activeTab){if(c.activeTab.destroyed){c.previousTab=null}else{c.previousTab=c.activeTab;c.activeTab.deactivate();c.deactivateFocusable(c.activeTab)}}b.activate();c.activateFocusable(b);c.activeTab=b;c.needsScroll=true;if(!a){c.fireEvent("change",c,b,b.card);c.updateLayout()}}},privates:{adjustTabPositions:function(){var h=this,a=h.items.items,d=a.length,g,b,e,c,j;if(!Ext.isIE8){j=h._getTabAdjustProp();while(d--){g=a[d];e=g.el;b=g.lastBox;c=g.isTab?g.getActualRotation():0;if(c===1&&g.isVisible()){e.setStyle(j,(b.x+b.width)+"px")}else{if(c===2&&g.isVisible()){e.setStyle(j,(b.x-b.height)+"px")}}}}},applyTargetCls:function(a){this.bodyTargetCls=a},_getTabAdjustProp:function(){return"left"},getTargetEl:function(){return this.body||this.frameBody||this.el},onClick:function(h,g){var d=this,i,c,b,a;if(h.getTarget(".x-box-scroller")){return}if(Ext.isIE8&&d.vertical){a=d.getTabInfoFromPoint(h.getXY());c=a.tab;b=a.close}else{i=h.getTarget("."+Ext.tab.Tab.prototype.baseCls);c=i&&Ext.getCmp(i.id);b=c&&c.closeEl&&(g===c.closeEl.dom)}if(b){h.preventDefault()}if(c&&c.isDisabled&&!c.isDisabled()){c.beforeClick(b);if(c.closable&&b){c.onCloseClick()}else{d.doActivateTab(c)}}},doActivateTab:function(a){var b=this.tabPanel;if(b){if(!a.disabled){b.setActiveTab(a.card)}}else{this.setActiveTab(a)}},onFocusableContainerFocus:function(c){var b=this,a=b.mixins.focusablecontainer,d;d=a.onFocusableContainerFocus.call(b,c);if(d&&d.isTab){b.doActivateTab(d)}},onFocusableContainerFocusEnter:function(c){var b=this,a=b.mixins.focusablecontainer,d;d=a.onFocusableContainerFocusEnter.call(b,c);if(d&&d.isTab){b.doActivateTab(d)}},focusChild:function(e,b){var c=this,a=c.mixins.focusablecontainer,d;d=a.focusChild.call(c,e,b);if(c.activateOnFocus&&d&&d.isTab){c.doActivateTab(d)}}}},0,["tabbar"],["component","box","container","tabbar"],{component:true,box:true,container:true,tabbar:true},["widget.tabbar"],[[Ext.util.FocusableContainer.prototype.mixinId||Ext.util.FocusableContainer.$className,Ext.util.FocusableContainer]],[Ext.tab,"Bar"],0));(Ext.cmd.derive("Ext.tab.Panel",Ext.panel.Panel,{alternateClassName:["Ext.TabPanel"],config:{tabBar:undefined,tabPosition:"top",tabRotation:"default",tabStretchMax:true},removePanelHeader:true,plain:false,itemCls:"x-tabpanel-child",minTabWidth:undefined,maxTabWidth:undefined,deferredRender:true,_defaultTabRotation:{top:0,right:1,bottom:0,left:2},initComponent:function(){var g=this,c=g.activeTab!==null?(g.activeTab||0):null,e=g.dockedItems,h=g.header,d=g.tabBarHeaderPosition,b=g.getTabBar(),a;g.layout=new Ext.layout.container.Card(Ext.apply({owner:g,deferredRender:g.deferredRender,itemCls:g.itemCls,activeItem:c},g.layout));if(d!=null){h=g.header=Ext.apply({},h);a=h.items=(h.items?h.items.slice():[]);h.itemPosition=d;a.push(b);h.hasTabBar=true}else{e=[].concat(g.dockedItems||[]);e.push(b);g.dockedItems=e}Ext.panel.Panel.prototype.initComponent.apply(this,arguments);c=g.activeTab=g.getComponent(c);if(c){b.setActiveTab(c.tab,true)}},onRender:function(){var b=this.items.items,a=b.length,c;Ext.panel.Panel.prototype.onRender.apply(this,arguments);for(c=0;cr){h=p.slice(r,u+1);s.add(h)}else{for(o=r-1;o>=t;o--){s.remove(s.items.items[o],false)}}}else{s.removeAll(false)}Ext.resumeLayouts(true);s.fireEvent("selectionchange",s,m,l);if(s._shouldFireChangeEvent){s.fireEvent("change",s,m,l)}s._shouldFireChangeEvent=true;s._needsSync=false},applyUseSplitButtons:function(b,a){if(this.rendered&&b!==a){Ext.raise("Cannot reconfigure 'useSplitButtons' config of Ext.toolbar.Breadcrumb after initial render")}return b},applyStore:function(a){if(a){a=Ext.data.StoreManager.lookup(a)}return a},updateStore:function(a,b){this._needsSync=true;if(a&&!this.isConfiguring){this.setSelection(a.getRoot())}},privates:{_onButtonClick:function(a,b){if(this.getUseSplitButtons()){this.setSelection(this.getStore().getNodeById(a._breadcrumbNodeId))}},_onMenuClick:function(c,a,b){if(a){this.setSelection(this.getStore().getNodeById(a._breadcrumbNodeId))}},_onMenuBeforeShow:function(c){var k=this,d=k.getStore().getNodeById(c.ownerCmp._breadcrumbNodeId),g=k.getDisplayField(),a=k.getShowMenuIcons(),o,b,l,j,e,m,n,h,p;if(d.hasChildNodes()){o=d.childNodes;j=[];for(e=0,h=o.length;e','
    lineempty" role="presentation">
    ',"",'
    -end-plus {expanderCls}" role="presentation">
    ','','
    {checkboxCls}-checked">
    ',"
    ",'',' role="presentation" class="{childCls} {baseIconCls} {customIconCls} ','{baseIconCls}-leafparent-expandedparent {iconCls}" ','style="background-image:url({icon})"/>>','','{value}',"",'{value}',""],uiFields:{checked:1,icon:1,iconCls:1},rowFields:{expanded:1,loaded:1,expandable:1,leaf:1,loading:1,qtip:1,qtitle:1,cls:1},initComponent:function(){var a=this;a.rendererScope=a.scope;a.setupRenderer();a.innerRenderer=a.renderer;a.renderer=a.treeRenderer;Ext.grid.column.Column.prototype.initComponent.call(this);a.scope=a;a.hasCustomRenderer=a.innerRenderer&&a.innerRenderer.length>1},treeRenderer:function(i,a,e,b,c,j,h){var g=this,k=e.get("cls"),d;if(a&&k){a.tdCls+=" "+k}d=g.initTemplateRendererData(i,a,e,b,c,j,h);return g.getTpl("cellTpl").apply(d)},initTemplateRendererData:function(l,a,g,b,e,m,j){var i=this,c=i.innerRenderer,d=g.data,k=g.parentNode,n=j.rootVisible,o=[],h;while(k&&(n||k.data.depth>0)){h=k.data;o[n?h.depth:h.depth-1]=h.isLast?0:1;k=k.parentNode}return{record:g,baseIconCls:i.iconCls,customIconCls:(d.icon||d.iconCls)?i.customIconCls:"",iconCls:d.iconCls,icon:d.icon,checkboxCls:i.checkboxCls,checked:d.checked,elbowCls:i.elbowCls,expanderCls:i.expanderCls,textCls:i.textCls,leaf:d.leaf,expandable:g.isExpandable(),expanded:d.expanded,isLast:g.isLastVisible(),blankUrl:Ext.BLANK_IMAGE_URL,href:d.href,hrefTarget:d.hrefTarget,lines:o,metaData:a,childCls:i.getChildCls?i.getChildCls()+" ":"",value:c?c.apply(i.rendererScope,arguments):l}},shouldUpdateCell:function(b,d){var e=this,c=0,a,g;if(d){a=d.length;for(;c1;Ext.grid.NavigationModel.prototype.initKeyNav.call(this,b);for(c=0,a=e.keyNav.length;c1},onCellClick:function(d,b,g,c,h,e,a){Ext.grid.NavigationModel.prototype.onCellClick.call(this,d,b,g,c,h,e,a);return !a.nodeToggled},onKeyLeft:function(d){var c=this,b=d.view,a=c.record;if(c.isTreeGrid&&!d.ctrlKey){return Ext.grid.NavigationModel.prototype.onKeyLeft.call(this,d)}if(d.position.column.isTreeColumn&&a.isExpanded()){b.collapse(a)}else{a=a.parentNode;if(a&&!(a.isRoot()&&!b.rootVisible)){c.setPosition(a,null,d)}}},onKeyRight:function(c){var b=this,a=b.record;if(b.isTreeGrid&&!c.ctrlKey){return Ext.grid.NavigationModel.prototype.onKeyRight.call(this,c)}if(!a.isLeaf()){if(c.position.column.isTreeColumn&&!a.isExpanded()){c.view.expand(a)}else{if(a.isExpanded()){a=a.childNodes[0];if(a){b.setPosition(a)}}}}},onKeyEnter:function(a){if(this.record.data.checked!=null){this.toggleCheck(a)}else{Ext.grid.NavigationModel.prototype.onKeyEnter.call(this,a)}},onKeySpace:function(a){if(this.record.data.checked!=null){this.toggleCheck(a)}else{Ext.grid.NavigationModel.prototype.onKeySpace.call(this,a)}},toggleCheck:function(a){this.view.onCheckChange(a)},onAsterisk:function(a){this.view.ownerCt.expandAll()}},0,0,0,0,["view.navigation.tree"],0,[Ext.tree,"NavigationModel"],0));(Ext.cmd.derive("Ext.tree.View",Ext.view.Table,{config:{selectionModel:{type:"treemodel"}},isTreeView:true,loadingCls:"x-grid-tree-loading",expandedCls:"x-grid-tree-node-expanded",leafCls:"x-grid-tree-node-leaf",expanderSelector:".x-tree-expander",checkboxSelector:".x-tree-checkbox",expanderIconOverCls:"x-tree-expander-over",nodeAnimWrapCls:"x-tree-animator-wrap",ariaRole:"tree",loadMask:false,rootVisible:true,expandDuration:250,collapseDuration:250,toggleOnDblClick:true,stripeRows:false,treeRowTpl:["{%","this.processRowValues(values);","this.nextTpl.applyOut(values, out, parent);","%}",{priority:10,processRowValues:function(c){var b=c.record,a=c.view;c.rowAttr["data-qtip"]=b.get("qtip")||"";c.rowAttr["data-qtitle"]=b.get("qtitle")||"";if(b.isExpanded()){c.rowClasses.push(a.expandedCls)}if(b.isLeaf()){c.rowClasses.push(a.leafCls)}if(b.isLoading()){c.rowClasses.push(a.loadingCls)}}}],initComponent:function(){var a=this;if(a.bufferedRenderer){a.animate=false}else{if(a.initialConfig.animate===undefined){a.animate=Ext.enableFx}}a.store=a.panel.getStore();a.onRootChange(a.store.getRoot());a.animQueue={};a.animWraps={};Ext.view.Table.prototype.initComponent.call(this);a.store.setRootVisible(a.rootVisible);a.addRowTpl(Ext.XTemplate.getTpl(a,"treeRowTpl"))},onFillComplete:function(e,d,b){var c=this,a=c.store,g=a.indexOf(b[0]);d.triggerUIUpdate();if(!b.length||g===-1){return}c.onAdd(c.store,b,g);c.refreshPartner()},refreshPartner:function(){var a=this.lockingPartner;if(a){a.refresh()}},afterComponentLayout:function(c,b,d,e){var a=this.getScrollable();Ext.view.Table.prototype.afterComponentLayout.call(this,c,b,d,e);if(a&&!this.bufferedRenderer){a.refresh()}},processUIEvent:function(a){if(a.getTarget("."+this.nodeAnimWrapCls,this.el)){return false}return Ext.view.Table.prototype.processUIEvent.call(this,a)},setRootNode:function(a){this.node=a},getChecked:function(){var a=[];this.node.cascadeBy(function(b){if(b.get("checked")){a.push(b)}});return a},isItemChecked:function(a){return a.get("checked")},createAnimWrap:function(a,b){var e=this,d=e.getNode(a),c;c=Ext.fly(d).insertSibling({role:"presentation",tag:"div",cls:e.nodeAnimWrapCls},"after");return{record:a,node:d,el:c,expanding:false,collapsing:false,animateEl:c,targetEl:c}},getAnimWrap:function(d,a){if(!this.animate){return null}var b=this.animWraps,c=b[d.internalId];if(a!==false){while(!c&&d){d=d.parentNode;if(d){c=b[d.internalId]}}}return c},doAdd:function(b,g){var h=this,d=b[0],i=d.parentNode,j=h.all,n,c=h.getAnimWrap(i),l,k,e,m,a;if(!c||!c.expanding){return Ext.view.Table.prototype.doAdd.call(this,b,g)}m=h.bufferRender(b,g,true);a=m.children;i=c.record;l=c.targetEl;k=l.dom.childNodes;e=k.length;n=g-h.indexInStore(i)-1;if(!e||n>=e){l.appendChild(m.fragment,true)}else{Ext.fly(k[n]).insertSibling(a,"before",true)}j.insert(g,a);return a},onRemove:function(g,a,b){var d=this,e,c,j=d.hasListeners.remove,h;if(d.viewReady){e=d.store.getCount()===0;if(d.bufferedRenderer){return Ext.view.Table.prototype.onRemove.call(this,g,a,b)}if(j){h=this.all.slice(b,b+a.length)}if(e){d.refresh()}else{for(c=a.length-1,b+=c;c>=0;--c,--b){d.doRemove(a[c],b)}d.refreshSizePending=true}if(j){d.fireEvent("itemremove",a,b,h,d)}}},doRemove:function(a,c){var h=this,d=h.all,b=h.getAnimWrap(a),g=d.item(c),e=g?g.dom:null;if(!e||!b||!b.collapsing){return Ext.view.Table.prototype.doRemove.call(this,a,c)}b.targetEl.dom.insertBefore(e,b.targetEl.dom.firstChild);d.removeElement(c)},onBeforeExpand:function(d,b,c){var e=this,a;if(e.rendered&&e.all.getCount()&&e.animate){if(e.getNode(d)){a=e.getAnimWrap(d,false);if(!a){a=e.animWraps[d.internalId]=e.createAnimWrap(d);a.animateEl.setHeight(0)}else{if(a.collapsing){a.targetEl.select(e.itemSelector).destroy()}}a.expanding=true;a.collapsing=false}}},onExpand:function(i){var h=this,e=h.animQueue,a=i.getId(),c=h.getNode(i),g=c?h.indexOf(c):-1,d,b,j;if(h.singleExpand){h.ensureSingleExpand(i)}if(g===-1){return}d=h.getAnimWrap(i,false);if(!d){i.isExpandingOrCollapsing=false;h.fireEvent("afteritemexpand",i,g,c);return}b=d.animateEl;j=d.targetEl;b.stopAnimation();e[a]=true;Ext.on("idle",function(){b.dom.style.height="0px"},null,{single:true});b.animate({from:{height:0},to:{height:j.dom.scrollHeight},duration:h.expandDuration,listeners:{afteranimate:function(){var k=j.dom.childNodes,l=Ext.Element.getActiveElement();if(k.length){if(!j.contains(l)){l=null}d.el.insertSibling(k,"before",true);if(l){Ext.fly(l).focus()}}d.el.destroy();h.animWraps[d.record.internalId]=e[a]=null}},callback:function(){i.isExpandingOrCollapsing=false;if(!h.destroyed){h.refreshSize(true)}h.fireEvent("afteritemexpand",i,g,c)}})},onBeforeCollapse:function(e,b,c,h,d){var g=this,a;if(g.rendered&&g.all.getCount()){if(g.animate){if(e.isVisible()){a=g.getAnimWrap(e);if(!a){a=g.animWraps[e.internalId]=g.createAnimWrap(e,c)}else{if(a.expanding){a.targetEl.select(this.itemSelector).destroy()}}a.expanding=false;a.collapsing=true;a.callback=h;a.scope=d}}else{g.onCollapseCallback=h;g.onCollapseScope=d}}},onCollapse:function(d){var g=this,a=g.animQueue,i=d.getId(),e=g.getNode(d),c=e?g.indexOf(e):-1,b=g.getAnimWrap(d),h;if(!g.all.getCount()||!d.isVisible()){return}if(!b){d.isExpandingOrCollapsing=false;g.fireEvent("afteritemcollapse",d,c,e);Ext.callback(g.onCollapseCallback,g.onCollapseScope);g.onCollapseCallback=g.onCollapseScope=null;return}h=b.animateEl;a[i]=true;h.stopAnimation();h.animate({to:{height:0},duration:g.collapseDuration,listeners:{afteranimate:function(){b.el.destroy();g.animWraps[b.record.internalId]=a[i]=null}},callback:function(){d.isExpandingOrCollapsing=false;if(!g.destroyed){g.refreshSize(true)}g.fireEvent("afteritemcollapse",d,c,e);Ext.callback(b.callback,b.scope);b.callback=b.scope=null}})},isAnimating:function(a){return !!this.animQueue[a.getId()]},expand:function(d,c,h,e){var g=this,b=!!g.animate,a;if(!b||!d.isExpandingOrCollapsing){if(!d.isLeaf()){d.isExpandingOrCollapsing=b}Ext.suspendLayouts();a=d.expand(c,h,e);Ext.resumeLayouts(true);return a}},collapse:function(c,b,g,d){var e=this,a=!!e.animate;if(!a||!c.isExpandingOrCollapsing){if(!c.isLeaf()){c.isExpandingOrCollapsing=a}return c.collapse(b,g,d)}},toggle:function(b,a,d,c){if(b.isExpanded()){this.collapse(b,a,d,c)}else{this.expand(b,a,d,c)}},onItemDblClick:function(a,g,c,h){var d=this,b=d.editingPlugin;Ext.view.Table.prototype.onItemDblClick.call(this,a,g,c,h);if(d.toggleOnDblClick&&a.isExpandable()&&!(b&&b.clicksToEdit===2)){d.toggle(a)}},onCellClick:function(i,g,b,k,h,c){var d=this,a=c.position.column,j;if(a.isTreeColumn){if(c.getTarget(d.checkboxSelector,i)&&Ext.isBoolean(j=b.get("checked"))&&d.fireEvent("beforecheckchange",b,j,c)!==false){d.onCheckChange(c);if(a.stopSelection){c.stopSelection=true}}else{if(c.getTarget(d.expanderSelector,i)&&b.isExpandable()){d.getNavigationModel().setPosition(c.position);d.toggle(b,c.ctrlKey);c.nodeToggled=true}}return Ext.view.Table.prototype.onCellClick.call(this,i,g,b,k,h,c)}},onCheckChange:function(c){var a=c.record,b=!a.get("checked");a.set("checked",b);this.fireEvent("checkchange",a,b,c)},onItemMouseOver:function(a,c,b,d){if(d.getTarget(this.expanderSelector,c)){d.getTarget(this.cellSelector,null,true).addCls(this.expanderIconOverCls)}},onItemMouseOut:function(a,c,b,d){if(d.getTarget(this.expanderSelector,c)){d.getTarget(this.cellSelector,null,true).removeCls(this.expanderIconOverCls)}},getStoreListeners:function(){return Ext.apply(Ext.view.Table.prototype.getStoreListeners.call(this),{rootchange:this.onRootChange,fillcomplete:this.onFillComplete})},onBindStore:function(a,c,d,e){var b=e&&e.getRootNode(),g=a&&a.getRootNode();Ext.view.Table.prototype.onBindStore.call(this,a,c,d,e);if(g!==b){this.onRootChange(g,b)}},onRootChange:function(d,a){var c=this,b=c.grid;if(a){c.rootListeners.destroy();c.rootListeners=null}if(d){c.rootListeners=d.on({beforeexpand:c.onBeforeExpand,expand:c.onExpand,beforecollapse:c.onBeforeCollapse,collapse:c.onCollapse,destroyable:true,scope:c});b.addRelayers(d)}},ensureSingleExpand:function(b){var a=b.parentNode;if(a){a.eachChild(function(c){if(c!==b&&c.isExpanded()){c.collapse()}})}}},0,["treeview"],["component","box","dataview","tableview","gridview","treeview"],{component:true,box:true,dataview:true,tableview:true,gridview:true,treeview:true},["widget.treeview"],0,[Ext.tree,"View"],0));(Ext.cmd.derive("Ext.tree.Panel",Ext.panel.Table,{alternateClassName:["Ext.tree.TreePanel","Ext.TreePanel"],viewType:"treeview",treeCls:"x-tree-panel",rowLines:false,lines:true,useArrows:false,singleExpand:false,ddConfig:{enableDrag:true,enableDrop:true},rootVisible:true,displayField:"text",root:null,normalCfgCopy:["displayField","root","singleExpand","useArrows","lines","rootVisible","scroll"],lockedCfgCopy:["displayField","root","singleExpand","useArrows","lines","rootVisible"],isTree:true,arrowCls:"x-tree-arrows",linesCls:"x-tree-lines",noLinesCls:"x-tree-no-lines",autoWidthCls:"x-autowidth-table",constructor:function(a){a=a||{};if(a.animate===undefined){a.animate=Ext.isBoolean(this.animate)?this.animate:Ext.enableFx}this.enableAnimations=a.animate;delete a.animate;Ext.panel.Table.prototype.constructor.call(this,a)},initComponent:function(){var d=this,b=[d.treeCls],c,a;if(d.useArrows){b.push(d.arrowCls);d.lines=false}if(d.lines){b.push(d.linesCls)}else{if(!d.useArrows){b.push(d.noLinesCls)}}c=d.applyStore(d.store);if(!c.getRoot()){c.setRoot({})}c.setRootVisible(d.rootVisible);d.viewConfig=Ext.apply({rootVisible:d.rootVisible,animate:d.enableAnimations,singleExpand:d.singleExpand,node:c.getRoot(),hideHeaders:d.hideHeaders,navigationModel:"tree"},d.viewConfig);if(!d.columns){if(d.initialConfig.hideHeaders===undefined){d.hideHeaders=true}d.addCls(d.autoWidthCls);d.columns=[{xtype:"treecolumn",text:"Name",flex:1,dataIndex:d.displayField}]}if(d.cls){b.push(d.cls)}d.cls=b.join(" ");Ext.panel.Table.prototype.initComponent.call(this);a=d.getView();d.relayEvents(a,["beforecheckchange","checkchange","afteritemexpand","afteritemcollapse"])},applyStore:function(a){var b=this;if(Ext.isString(a)){a=b.store=Ext.StoreMgr.lookup(a)}else{if(!a||!a.isStore){a=Ext.apply({type:"tree",proxy:"memory"},a);if(b.root){a.root=b.root}if(b.fields){a.fields=b.fields}else{if(b.model){a.model=b.model}}if(b.folderSort){a.folderSort=b.folderSort}a=b.store=Ext.StoreMgr.lookup(a)}else{if(b.root){a=b.store=Ext.data.StoreManager.lookup(a);a.setRoot(b.root);if(b.folderSort!==undefined){a.folderSort=b.folderSort;a.sort()}}}}return a},setStore:function(a){var b=this;a=b.applyStore(a);if(!a.getRoot()){a.setRoot({})}a.setRootVisible(b.rootVisible);if(b.view){b.view.setRootNode(a.getRootNode())}b.bindStore(a)},bindStore:function(b,c){var d=this,a=b.getRoot(),e=d.bufferedRenderer;Ext.panel.Table.prototype.bindStore.apply(this,arguments);if(e){if(e.store){e.bindStore(b)}}b.singleExpand=d.singleExpand;d.storeListeners=d.mon(b,{destroyable:true,rootchange:d.onRootChange,scope:d});d.storeRelayers=d.relayEvents(b,["beforeload","load"]);if(!d.rootVisible&&!b.autoLoad&&!(a.isExpanded()||a.isLoading())){if(a.isLoaded()){a.data.expanded=true;b.onNodeExpand(a,a.childNodes)}else{if(b.autoLoad!==false){a.data.expanded=false;a.expand()}}}b.ownerTree=d;if(!c){d.view.setRootNode(a)}},addRelayers:function(b){var a=this;if(a.rootRelayers){a.rootRelayers.destroy();a.rootRelayers=null}a.rootRelayers=a.mon(b,{destroyable:true,append:a.createRelayer("itemappend"),remove:a.createRelayer("itemremove"),move:a.createRelayer("itemmove",[0,4]),insert:a.createRelayer("iteminsert"),beforeappend:a.createRelayer("beforeitemappend"),beforeremove:a.createRelayer("beforeitemremove"),beforemove:a.createRelayer("beforeitemmove"),beforeinsert:a.createRelayer("beforeiteminsert"),expand:a.createRelayer("itemexpand",[0,1]),collapse:a.createRelayer("itemcollapse",[0,1]),beforeexpand:a.createRelayer("beforeitemexpand",[0,1]),beforecollapse:a.createRelayer("beforeitemcollapse",[0,1]),scope:a})},unbindStore:function(){var b=this,a=b.store;if(a){Ext.panel.Table.prototype.unbindStore.call(this);Ext.destroy(b.storeListeners,b.storeRelayers,b.rootRelayers);delete a.ownerTree;a.singleExpand=null}},setRootNode:function(){return this.store.setRoot.apply(this.store,arguments)},getRootNode:function(){return this.store.getRoot()},onRootChange:function(a){this.view.setRootNode(a)},getChecked:function(){return this.getView().getChecked()},isItemChecked:function(a){return a.get("checked")},expandNode:function(b,a,d,c){return this.getView().expand(b,a,d,c||this)},collapseNode:function(b,a,d,c){return this.getView().collapse(b,a,d,c||this)},expandAll:function(d,b){var c=this,a=c.getRootNode();if(a){Ext.suspendLayouts();a.expand(true,d,b||c);Ext.resumeLayouts(true)}},collapseAll:function(e,c){var d=this,b=d.getRootNode(),a=d.getView();if(b){Ext.suspendLayouts();c=c||d;if(a.rootVisible){b.collapse(true,e,c)}else{b.collapseChildren(true,e,c)}Ext.resumeLayouts(true)}},expandPath:function(o,q){var e=arguments,g=this,h=g.view,i=(q&&q.field)||g.store.model.idProperty,j,p,b=(q&&q.separator)||"/",k,n,d,c,m,a,l;if(q&&typeof q==="object"){i=q.field||g.store.model.idProperty;b=q.separator||"/";k=q.callback;n=q.scope;j=q.select;p=q.focus}else{i=e[1]||g.store.model.idProperty;b=e[2]||"/";k=e[3];n=e[4]}if(Ext.isEmpty(o)){return Ext.callback(k,n||g,[false,null])}m=o.split(b);a=!m[0];if(a){d=g.getRootNode();c=1}else{d=g.store.findNode(i,m[0]);c=0}if(!d||(a&&d.get(i)!==m[1])){return Ext.callback(k,n||g,[false,d])}l=function(v){var t=this,r,s,u;if(++c===m.length){if(j){h.getSelectionModel().select(t)}if(p){h.getNavigationModel().setPosition(t,0)}return Ext.callback(k,n||g,[true,t,h.getNode(t)])}for(s=0,r=v?v.length:0;s=i.top&&h<(i.top+d)){return"before"}else{if(!a&&(k||(h>=(i.bottom-d)&&h<=i.bottom))){return"after"}else{return"append"}}},isValidDropPoint:function(b,j,n,k,g){if(!b||!g.item){return false}var o=this.view,l=o.getRecord(b),d=g.records,a=d.length,m=d.length,c,h;if(!(l&&j&&a)){return false}for(c=0;c=0;--l){o=p[l];if(o.styleSheet){c.cacheStyleSheet(o.styleSheet)}c.cacheRule(o,m)}}catch(n){}},cacheRule:function(h,l){if(h.styleSheet){return c.cacheStyleSheet(h.styleSheet)}var k=h.selectorText,i,g;if(k){k=k.split(",");i=k.length;for(g=0;g2)?a[2]:null,h=(i>3)?a[3]:"/",d=(i>4)?a[4]:null,g=(i>5)?a[5]:false;document.cookie=c+"="+escape(e)+((b===null)?"":("; expires="+b.toUTCString()))+((h===null)?"":("; path="+h))+((d===null)?"":("; domain="+d))+((g===true)?"; secure":"")},get:function(c){var g=document.cookie.split("; "),a=g.length,e,d,b;for(d=0;d'+this.removeRowText+""},beforeDestroy:function(){Ext.un({mousedown:"onDismissSearch",scope:this});Ext.grid.Panel.prototype.beforeDestroy.call(this)},privates:{onDismissSearch:function(b){var a=this.searchPopup;if(a&&!(a.owns(b.getTarget())||this.owns(b.getTarget()))){Ext.un({mousedown:"onDismissSearch",scope:this});a.hide()}},onShowSearch:function(b,d){var e=this,a=e.searchPopup,c=e.getStore();if(!a){a=Ext.merge({owner:e,field:e.fieldName,floating:true},e.getSearch());e.searchPopup=a=e.add(a);if(c.getCount()){a.selectRecords(c.getRange())}}a.showBy(e,"tl-tr?");Ext.on({mousedown:"onDismissSearch",scope:e})}}},0,["multiselector"],["component","box","container","panel","tablepanel","gridpanel","grid","multiselector"],{component:true,box:true,container:true,panel:true,tablepanel:true,gridpanel:true,grid:true,multiselector:true},["widget.multiselector"],0,[Ext.view,"MultiSelector"],0));(Ext.cmd.derive("Ext.window.Toast",Ext.window.Window,{isToast:true,cls:"x-toast",bodyPadding:10,autoClose:true,plain:false,draggable:false,resizable:false,shadow:false,focus:Ext.emptyFn,anchor:null,useXAxis:false,align:"br",animate:true,spacing:6,paddingX:30,paddingY:10,slideInAnimation:"easeIn",slideBackAnimation:"bounceOut",slideInDuration:500,slideBackDuration:500,hideDuration:500,autoCloseDelay:3000,stickOnClick:true,stickWhileHover:true,closeOnMouseDown:false,closable:false,isHiding:false,isFading:false,destroyAfterHide:false,closeOnMouseOut:false,xPos:0,yPos:0,initComponent:function(){var a=this;if(a.autoClose&&!a.hasOwnProperty("closable")){a.closable=false}a.updateAlignment(a.align);a.setAnchor(a.anchor);Ext.window.Window.prototype.initComponent.call(this)},onRender:function(){var a=this;Ext.window.Window.prototype.onRender.apply(this,arguments);a.el.hover(a.onMouseEnter,a.onMouseLeave,a);if(a.closeOnMouseDown){Ext.getDoc().on("mousedown",a.onDocumentMousedown,a)}},alignmentProps:{br:{paddingFactorX:-1,paddingFactorY:-1,siblingAlignment:"br-br",anchorAlign:"tr-br"},bl:{paddingFactorX:1,paddingFactorY:-1,siblingAlignment:"bl-bl",anchorAlign:"tl-bl"},tr:{paddingFactorX:-1,paddingFactorY:1,siblingAlignment:"tr-tr",anchorAlign:"br-tr"},tl:{paddingFactorX:1,paddingFactorY:1,siblingAlignment:"tl-tl",anchorAlign:"bl-tl"},b:{paddingFactorX:0,paddingFactorY:-1,siblingAlignment:"b-b",useXAxis:0,anchorAlign:"t-b"},t:{paddingFactorX:0,paddingFactorY:1,siblingAlignment:"t-t",useXAxis:0,anchorAlign:"b-t"},l:{paddingFactorX:1,paddingFactorY:0,siblingAlignment:"l-l",useXAxis:1,anchorAlign:"r-l"},r:{paddingFactorX:-1,paddingFactorY:0,siblingAlignment:"r-r",useXAxis:1,anchorAlign:"l-r"},x:{br:{anchorAlign:"bl-br"},bl:{anchorAlign:"br-bl"},tr:{anchorAlign:"tl-tr"},tl:{anchorAlign:"tr-tl"}}},updateAlignment:function(e){var c=this,a=c.alignmentProps,b=a[e],d=a.x[e];if(d&&c.useXAxis){Ext.applyIf(c,d)}Ext.applyIf(c,b)},getXposAlignedToAnchor:function(){var c=this,g=c.align,a=c.anchor,d=a&&a.el,b=c.el,e=0;if(d&&d.dom){if(!c.useXAxis){e=b.getLeft()}else{if(g==="br"||g==="tr"||g==="r"){e+=d.getAnchorXY("r")[0];e-=(b.getWidth()+c.paddingX)}else{e+=d.getAnchorXY("l")[0];e+=c.paddingX}}}return e},getYposAlignedToAnchor:function(){var d=this,g=d.align,a=d.anchor,e=a&&a.el,b=d.el,c=0;if(e&&e.dom){if(d.useXAxis){c=b.getTop()}else{if(g==="br"||g==="bl"||g==="b"){c+=e.getAnchorXY("b")[1];c-=(b.getHeight()+d.paddingY)}else{c+=e.getAnchorXY("t")[1];c+=d.paddingY}}}return c},getXposAlignedToSibling:function(b){var c=this,e=c.align,a=c.el,d;if(!c.useXAxis){d=a.getLeft()}else{if(e==="tl"||e==="bl"||e==="l"){d=(b.xPos+b.el.getWidth()+b.spacing)}else{d=(b.xPos-a.getWidth()-c.spacing)}}return d},getYposAlignedToSibling:function(b){var d=this,e=d.align,a=d.el,c;if(d.useXAxis){c=a.getTop()}else{if(e==="tr"||e==="tl"||e==="t"){c=(b.yPos+b.el.getHeight()+b.spacing)}else{c=(b.yPos-a.getHeight()-b.spacing)}}return c},getToasts:function(){var a=this.anchor,c=this.anchorAlign,b=a.activeToasts||(a.activeToasts={});return b[c]||(b[c]=[])},setAnchor:function(a){var c=this,b;c.anchor=a=((typeof a==="string")?Ext.getCmp(a):a);if(!a){b=Ext.window.Toast;c.anchor=b.bodyAnchor||(b.bodyAnchor={el:Ext.getBody()})}},beforeShow:function(){var a=this;if(a.stickOnClick){a.body.on("click",function(){a.cancelAutoClose()})}if(a.autoClose){if(!a.closeTask){a.closeTask=new Ext.util.DelayedTask(a.doAutoClose,a)}}a.el.setX(-10000);a.el.setOpacity(1)},afterShow:function(){var e=this,b=e.el,d,a,c,g;Ext.window.Window.prototype.afterShow.apply(this,arguments);d=e.getToasts();c=d.length;a=c&&d[c-1];if(a){b.alignTo(a.el,e.siblingAlignment,[0,0]);e.xPos=e.getXposAlignedToSibling(a);e.yPos=e.getYposAlignedToSibling(a)}else{b.alignTo(e.anchor.el,e.anchorAlign,[(e.paddingX*e.paddingFactorX),(e.paddingY*e.paddingFactorY)],false);e.xPos=e.getXposAlignedToAnchor();e.yPos=e.getYposAlignedToAnchor()}Ext.Array.include(d,e);if(e.animate){g=b.getXY();b.animate({from:{x:g[0],y:g[1]},to:{x:e.xPos,y:e.yPos,opacity:1},easing:e.slideInAnimation,duration:e.slideInDuration,dynamic:true,callback:e.afterPositioned,scope:e})}else{e.setLocalXY(e.xPos,e.yPos);e.afterPositioned()}},afterPositioned:function(){if(this.autoClose){this.closeTask.delay(this.autoCloseDelay)}},onDocumentMousedown:function(a){if(this.isVisible()&&!this.owns(a.getTarget())){this.hide()}},slideBack:function(){var e=this,b=e.anchor,g=b&&b.el,c=e.el,d=e.getToasts(),a=Ext.Array.indexOf(d,e);if(!e.isHiding&&c&&c.dom&&g&&g.isVisible()){if(a){e.xPos=e.getXposAlignedToSibling(d[a-1]);e.yPos=e.getYposAlignedToSibling(d[a-1])}else{e.xPos=e.getXposAlignedToAnchor();e.yPos=e.getYposAlignedToAnchor()}e.stopAnimation();if(e.animate){c.animate({to:{x:e.xPos,y:e.yPos},easing:e.slideBackAnimation,duration:e.slideBackDuration,dynamic:true})}}},update:function(){var a=this;if(a.isVisible()){a.isHiding=true;a.hide()}Ext.window.Window.prototype.update.apply(this,arguments);a.show()},cancelAutoClose:function(){var a=this.closeTask;if(a){a.cancel()}},doAutoClose:function(){var a=this;if(!(a.stickWhileHover&&a.mouseIsOver)){a.close()}else{a.closeOnMouseOut=true}},onMouseEnter:function(){this.mouseIsOver=true},onMouseLeave:function(){var a=this;a.mouseIsOver=false;if(a.closeOnMouseOut){a.closeOnMouseOut=false;a.close()}},removeFromAnchor:function(){var c=this,b,a;if(c.anchor){b=c.getToasts();a=Ext.Array.indexOf(b,c);if(a!==-1){Ext.Array.erase(b,a,1);for(;a1?parseInt(F[2]):0;if(K){D=true}}else{K=0}E[N]=K}if(E.ie){var G=document.documentMode;if(G>=8){E.ie=G}}K=E.ie||false;L=Math.max(K,w.maxIEVersion);for(I=8;I<=L;++I){H="ie"+I;E[H+"m"]=K?K<=I:0;E[H]=K?K===I:0;E[H+"p"]=K?K>=I:0}return E},y=function(){var E={},J,K,M,G,H,F,D,I,L;M=c(w.osPrefixes);H=M.length;for(G=0,L=0;G1?parseFloat(F[F.length-1]):0}if(I){L++}E[K]=I}M=c(w.fallbackOSPrefixes);H=M.length;for(G=0;G1){z=u[1];if(z==="false"||z==="0"){z=false}}y[s]=z}}return y},filterPlatform:function(t,x){t=f.concat(t||f);x=f.concat(x||f);var w=t.length,v=x.length,s=(!w&&v),u,r;for(u=0;u0&&((v=t.charAt(w-1))==="?"||v==="&")){r=t.indexOf("&",w);r=(r<0)?"":t.substring(r);if(r&&v==="?"){++w;r=r.substring(1)}t=t.substring(0,w-1)+r}return t},getConfig:function(r){return r?d.config[r]:d.config},setConfig:function(r,u){if(typeof r==="string"){d.config[r]=u}else{for(var t in r){d.setConfig(t,r[t])}}return d},getHead:function(){return d.docHead||(d.docHead=o.head||o.getElementsByTagName("head")[0])},create:function(t,u,r){var s=r||{};s.url=t;s.key=u;return d.scripts[u]=new j(s)},getEntry:function(s,r){var t=d.canonicalUrl(s),u=d.scripts[t];if(!u){u=d.create(s,t,r)}return u},registerContent:function(s,t,u){var r={content:u,loaded:true,css:t==="css"};return d.getEntry(s,r)},processRequest:function(s,r){s.loadEntries(r)},load:function(r){e("Boot.load called");var r=new b(r);if(r.sync||d.syncMode){return d.loadSync(r)}if(d.currentRequest){e("current active request, suspending this request");r.getEntries();d.suspendedQueue.push(r)}else{d.currentRequest=r;d.processRequest(r,false)}return d},loadSync:function(r){e("Boot.loadSync called");var r=new b(r);d.syncMode++;d.processRequest(r,true);d.syncMode--;return d},loadBasePrefix:function(r){r=new b(r);r.prependBaseUrl=true;return d.load(r)},loadSyncBasePrefix:function(r){r=new b(r);r.prependBaseUrl=true;return d.loadSync(r)},requestComplete:function(s){var r;if(d.currentRequest===s){d.currentRequest=null;while(d.suspendedQueue.length>0){r=d.suspendedQueue.shift();if(!r.done){e("resuming suspended request");d.load(r);break}}}if(!d.currentRequest&&d.suspendedQueue.length==0){d.fireListeners()}},isLoading:function(){return !d.currentRequest&&d.suspendedQueue.length==0},fireListeners:function(){var r;while(d.isLoading()&&(r=d.listeners.shift())){r()}},onBootReady:function(r){if(!d.isLoading()){r()}else{d.listeners.push(r)}},getPathsFromIndexes:function(s,r){return b.prototype.getPathsFromIndexes(s,r)},createLoadOrderMap:function(r){return b.prototype.createLoadOrderMap(r)},fetch:function(r,s,A,u){u=(u===undefined)?!!s:u;var z=new XMLHttpRequest(),B,w,x,t=false,y=function(){if(z&&z.readyState==4){w=(z.status===1223)?204:(z.status===0&&((self.location||{}).protocol==="file:"||(self.location||{}).protocol==="ionp:"))?200:z.status;x=z.responseText;B={content:x,status:w,exception:t};if(s){s.call(A,B)}z=null}};if(u){z.onreadystatechange=y}try{e("fetching "+r+" "+(u?"async":"sync"));z.open("GET",r,u);z.send(null)}catch(v){t=v;y();return B}if(!u){y()}return B},notifyAll:function(r){r.notifyRequests()}};function b(r){if(r.$isRequest){return r}var r=r.url?r:{url:r},s=r.url,t=s.charAt?[s]:s,u=r.charset||d.config.charset;a(r,{urls:t,charset:u});a(this,r)}b.prototype={$isRequest:true,createLoadOrderMap:function(s){var r=s.length,t={},v,u;for(v=0;v0){setTimeout(function(){u.call(t,v)},r)}else{u.call(t,v)}}v.fireListeners();d.requestComplete(v)}},onDone:function(t){var s=this,r=s.listeners||(s.listeners=[]);if(s.done){t(s)}else{r.push(t)}},fireListeners:function(){var r=this.listeners,s;if(r){e("firing request listeners");while((s=r.shift())){s(this)}}}};function j(s){if(s.$isEntry){return s}e("creating entry for "+s.url);var x=s.charset||d.config.charset,w=Ext.manifest,r=w&&w.loader,t=(s.cache!==undefined)?s.cache:(r&&r.cache),v,u;if(d.config.disableCaching){if(t===undefined){t=!d.config.disableCaching}if(t===false){v=+new Date()}else{if(t!==true){v=t}}if(v){u=(r&&r.cacheParam)||d.config.disableCachingParam;v=u+"="+v}}a(s,{charset:x,buster:v,requests:[]});a(this,s)}j.prototype={$isEntry:true,done:false,evaluated:false,loaded:false,isCrossDomain:function(){var r=this;if(r.crossDomain===undefined){e("checking "+r.getLoadUrl()+" for prefix "+d.origin);r.crossDomain=(r.getLoadUrl().indexOf(d.origin)!==0)}return r.crossDomain},isCss:function(){var s=this;if(s.css===undefined){if(s.url){var r=d.assetConfig[s.url];s.css=r?r.type==="css":g.test(s.url)}else{s.css=false}}return this.css},getElement:function(r){var t=this,s=t.el;if(!s){e("creating element for "+t.url);if(t.isCss()){r=r||"link";s=o.createElement(r);if(r=="link"){s.rel="stylesheet";t.prop="href"}else{t.prop="textContent"}s.type="text/css"}else{r=r||"script";s=o.createElement(r);s.type="text/javascript";t.prop="src";if(t.charset){s.charset=t.charset}if(d.hasAsync){s.async=false}}t.el=s}return s},getLoadUrl:function(){var s=this,r=d.canonicalUrl(s.url);if(!s.loadUrl){s.loadUrl=!!s.buster?(r+(r.indexOf("?")===-1?"?":"&")+s.buster):r}return s.loadUrl},fetch:function(u){var s=this.getLoadUrl(),t=!!u.async,r=u.complete;d.fetch(s,r,this,t)},onContentLoaded:function(s){var w=this,r=s.status,v=s.content,u=s.exception,t=this.getLoadUrl();w.loaded=true;if((u||r===0)&&!i.phantom){w.error=("Failed loading synchronously via XHR: '"+t+"'. It's likely that the file is either being loaded from a different domain or from the local file system where cross origin requests are not allowed for security reasons. Try asynchronous loading instead.")||true;w.evaluated=true}else{if((r>=200&&r<300)||r===304||i.phantom||(r===0&&v.length>0)){w.content=v}else{w.error=("Failed loading synchronously via XHR: '"+t+"'. Please verify that the file exists. XHR status code: "+r)||true;w.evaluated=true}}},createLoadElement:function(v){var t=this,s=t.getElement(),r=function(){if(this.readyState==="loaded"||this.readyState==="complete"){if(v){v()}}},u=function(){t.error=true;if(v){v()}};t.preserve=true;s.onerror=u;if(d.hasReadyState){s.onreadystatechange=r}else{s.onload=v}s[t.prop]=t.getLoadUrl()},onLoadElementReady:function(){d.getHead().appendChild(this.getElement());this.evaluated=true},inject:function(w,v){e("injecting content for "+this.url);var x=this,y=d.getHead(),r=x.url,z=x.key,s,t,u,A;if(x.isCss()){x.preserve=true;A=z.substring(0,z.lastIndexOf("/")+1);s=o.createElement("base");s.href=A;if(y.firstChild){y.insertBefore(s,y.firstChild)}else{y.appendChild(s)}s.href=s.href;if(r){w+="\n/*# sourceURL="+z+" */"}t=x.getElement("style");u=("styleSheet" in t);y.appendChild(s);if(u){y.appendChild(t);t.styleSheet.cssText=w}else{t.textContent=w;y.appendChild(t)}y.removeChild(s)}else{if(r){w+="\n//# sourceURL="+z}Ext.globalEval(w)}return x},loadCrossDomain:function(){var s=this,r=function(){s.loaded=s.evaluated=s.done=true;s.notifyRequests()};s.createLoadElement(function(){r()});s.evaluateLoadElement();return false},loadElement:function(){var s=this,r=function(){s.loaded=s.evaluated=s.done=true;s.notifyRequests()};s.createLoadElement(function(){r()});s.evaluateLoadElement();return true},loadSync:function(){var r=this;r.fetch({async:false,complete:function(s){r.onContentLoaded(s)}});r.evaluate();r.notifyRequests()},load:function(s){var r=this;if(!r.loaded){if(r.loading){return false}r.loading=true;if(!s){if(r.isCrossDomain()){return r.loadCrossDomain()}else{if(!r.isCss()&&d.hasReadyState){r.createLoadElement(function(){r.loaded=true;r.notifyRequests()})}else{if(d.useElements&&!(r.isCss()&&i.phantom)){return r.loadElement()}else{r.fetch({async:!s,complete:function(t){r.onContentLoaded(t);r.notifyRequests()}})}}}}else{r.loadSync()}}return true},evaluateContent:function(){this.inject(this.content);this.content=null},evaluateLoadElement:function(){d.getHead().appendChild(this.getElement())},evaluate:function(){var r=this;if(!r.evaluated){if(r.evaluating){return}r.evaluating=true;if(r.content!==undefined){r.evaluateContent()}else{if(!r.error){r.evaluateLoadElement()}}r.evaluated=r.done=true;r.cleanup()}},cleanup:function(){var t=this,s=t.el,u;if(!s){return}if(!t.preserve){t.el=null;s.parentNode.removeChild(s);for(u in s){try{if(u!==t.prop){s[u]=null}delete s[u]}catch(r){}}}s.onload=s.onerror=s.onreadystatechange=h},notifyRequests:function(){var u=this.requests,r=u.length,s,t;for(s=0;s0){e("firing event listeners for url "+this.url);while((s=r.shift())){s(this)}}}};Ext.disableCacheBuster=function(s,t){var r=new Date();r.setTime(r.getTime()+(s?10*365:-1)*24*60*60*1000);r=r.toGMTString();o.cookie="ext-cache=1; expires="+r+"; path="+(t||"/")};d.init();return d}(function(){}));Ext.globalEval=Ext.globalEval||(this.execScript?function(a){execScript(a)}:function($$code){eval.call(window,$$code)});if(!Function.prototype.bind){(function(){var a=Array.prototype.slice,b=function(d){var c=a.call(arguments,1),e=this;if(c.length){return function(){var f=arguments;return e.apply(d,f.length?c.concat(a.call(f)):c)}}c=null;return function(){return e.apply(d,arguments)}};Function.prototype.bind=b;b.$extjs=true}())}Ext.setResourcePath=function(c,b){var a=Ext.manifest||(Ext.manifest={}),d=a.resources||(a.resources={});if(a){if(typeof c!=="string"){Ext.apply(d,c)}else{d[c]=b}a.resources=d}};Ext.getResourcePath=function(f,e,a){if(typeof f!=="string"){e=f.pool;a=f.packageName;f=f.path}var d=Ext.manifest,g=d&&d.resources,c=g[e],b=[];if(c==null){c=g.path;if(c==null){c="resources"}}if(c){b.push(c)}if(a){b.push(a)}b.push(f);return b.join("/")};var Ext=Ext||{};(function(){var b=this,g=Object.prototype,c=g.toString,n=["valueOf","toLocaleString","toString","constructor"],k=function(){},f=function(){},h=function(i){return i},m=function(){var i=m.caller.caller;return i.$owner.prototype[i.$name].apply(this,arguments)},a=Ext.manifest||{},j,d=/\[object\s*(?:Array|Arguments|\w*Collection|\w*List|HTML\s+document\.all\s+class)\]/,e=/^\\?\/Date\(([-+])?(\d+)(?:[+-]\d{4})?\)\\?\/$/;Ext.global=b;Ext.now=Date.now||(Date.now=function(){return +new Date()});Ext.ticks=(b.performance&&b.performance.now)?function(){return performance.now()}:Ext.now;Ext._startTime=Ext.ticks();k.$nullFn=h.$nullFn=k.$emptyFn=h.$identityFn=f.$nullFn=true;f.$privacy="framework";Ext.suspendLayouts=Ext.resumeLayouts=k;for(j in {toString:1}){n=null}Ext.enumerables=n;Ext.apply=function(r,q,t){if(t){Ext.apply(r,t)}if(r&&q&&typeof q==="object"){var s,p,o;for(s in q){r[s]=q[s]}if(n){for(p=n.length;p--;){o=n[p];if(q.hasOwnProperty(o)){r[o]=q[o]}}}}return r};function l(q,i,r){var o,p;for(o in r){if(r.hasOwnProperty(o)){p=r[o];if(typeof p==="function"){if(i.$className){p.name=i.$className+"#"+o}p.$name=o;p.$owner=i;p.$previous=q.hasOwnProperty(o)?q[o]:m}q[o]=p}}}Ext.buildSettings=Ext.apply({baseCSSPrefix:"x-"},Ext.buildSettings||{});Ext.apply(Ext,{idSeed:0,idPrefix:"ext-",isSecure:/^https/i.test(window.location.protocol),enableGarbageCollector:false,enableListenerCollection:true,name:Ext.sandboxName||"Ext",privateFn:f,emptyFn:k,identityFn:h,frameStartTime:Ext.now(),manifest:a,debugConfig:Ext.debugConfig||a.debug||{hooks:{"*":true}},enableAria:true,enableAriaButtons:true,enableAriaPanels:true,startsWithHashRe:/^#/,validIdRe:/^[a-z_][a-z0-9\-_]*$/i,BLANK_IMAGE_URL:"data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==",makeIdSelector:function(i){if(!Ext.validIdRe.test(i)){Ext.raise('Invalid id selector: "'+i+'"')}return"#"+i},id:function(p,i){if(p&&p.id){return p.id}var q=(i||Ext.idPrefix)+(++Ext.idSeed);if(p){p.id=q}return q},returnId:function(i){return i.getId()},returnTrue:function(){return true},emptyString:new String(),baseCSSPrefix:Ext.buildSettings.baseCSSPrefix,$eventNameMap:{},$vendorEventRe:/^(Moz.+|MS.+|webkit.+)/,canonicalEventName:function(i){return Ext.$eventNameMap[i]||(Ext.$eventNameMap[i]=(Ext.$vendorEventRe.test(i)?i:i.toLowerCase()))},applyIf:function(o,i){var p;if(o){for(p in i){if(o[p]===undefined){o[p]=i[p]}}}return o},destroy:function(){var q=arguments.length,p,o;for(p=0;p0){r--;p[r]="var Ext=window."+Ext.name+";"+p[r]}}i=p.join("");q=o[i];if(!q){q=Function.prototype.constructor.apply(Function.prototype,p);o[i]=q}return q},functionFactory:function(){var i=Array.prototype.slice.call(arguments),o;if(Ext.isSandboxed){o=i.length;if(o>0){o--;i[o]="var Ext=window."+Ext.name+";"+i[o]}}return Function.prototype.constructor.apply(Function.prototype,i)},Logger:{log:function(o,i){if(o&&b.console){if(!i||!(i in b.console)){i="log"}o="["+i.toUpperCase()+"] "+o;b.console[i](o)}},verbose:function(i){this.log(i,"verbose")},info:function(i){this.log(i,"info")},warn:function(i){this.log(i,"warn")},error:function(i){throw new Error(i)},deprecate:function(i){this.log(i,"warn")}}||{verbose:k,log:k,info:k,warn:k,error:function(i){throw new Error(i)},deprecate:k},getElementById:function(i){return document.getElementById(i)},splitAndUnescape:(function(){var i={};return function(q,p){if(!q){return[]}else{if(!p){return[q]}}var s=i[p]||(i[p]=new RegExp("\\\\"+p,"g")),o=[],t,r;t=q.split(p);while((r=t.shift())!==undefined){while(r.charAt(r.length-1)==="\\"&&t.length>0){r=r+p+t.shift()}r=r.replace(s,p);o.push(r)}return o}})()});Ext.returnTrue.$nullFn=Ext.returnId.$nullFn=true}());Ext.platformTags.modern=!(Ext.platformTags.classic=Ext.isClassic=true);(function(){function a(){var c=this,b=c.sourceClass,e=c.sourceMethod,d=c.msg;if(e){if(d){e+="(): ";e+=d}else{e+="()"}}if(b){e=e?(b+"."+e):b}return e||d||""}Ext.Error=function(c){if(Ext.isString(c)){c={msg:c}}var b=new Error();Ext.apply(b,c);b.message=b.message||b.msg;b.toString=a;return b};Ext.apply(Ext.Error,{ignore:false,raise:function(d){d=d||{};if(Ext.isString(d)){d={msg:d}}var c=this,f=c.raise.caller,e,b;if(f===Ext.raise){f=f.caller}if(f){if(!d.sourceMethod&&(b=f.$name)){d.sourceMethod=b}if(!d.sourceClass&&(b=f.$owner)&&(b=b.$className)){d.sourceClass=b}}if(c.handle(d)!==true){e=a.call(d);Ext.log({msg:e,level:"error",dump:d,stack:true});throw new Ext.Error(d)}},handle:function(){return this.ignore}})})();Ext.deprecated=function(b){if(!b){b=""}function a(){Ext.raise('The method "'+a.$owner.$className+"."+a.$name+'" has been removed. '+b)}return a;return Ext.emptyFn};Ext.raise=function(){Ext.Error.raise.apply(Ext.Error,arguments)};(function(){if(typeof window==="undefined"){return}var b=0,a=function(){var c=Ext.log&&Ext.log.counters,e=c&&(c.error+c.warn+c.info+c.log),d;if(e&&b!==e){d=[];if(c.error){d.push("Errors: "+c.error)}if(c.warn){d.push("Warnings: "+c.warn)}if(c.info){d.push("Info: "+c.info)}if(c.log){d.push("Log: "+c.log)}window.status="*** "+d.join(" -- ");b=e}};setInterval(a,1000)}());Ext.Array=(function(){var c=Array.prototype,k=c.slice,m=(function(){var u=[],e,t=20;if(!u.splice){return false}while(t--){u.push("A")}u.splice(15,0,"F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F");e=u.length;u.splice(13,0,"XXX");if(e+1!==u.length){return false}return true}()),l="indexOf" in c,g=true;function j(w,t){var e=w.length,v=new Array(e),u;for(u=0;uC){for(x=e;x--;){A[u+x]=A[C+x]}}}if(E&&B===w){A.length=w;A.push.apply(A,D)}else{A.length=w+E;for(x=0;x>1;x=y(w,z[t]);if(x>=0){u=t+1}else{if(x<0){e=t-1}}}return u},defaultCompare:function(e,t){return(et)?1:0)},lexicalCompare:function(e,t){e=String(e);t=String(t);return(et)?1:0)},each:function(x,v,u,e){x=a.from(x);var t,w=x.length;if(e!==true){for(t=0;t-1;t--){if(v.call(u||x[t],x[t],t,x)===false){return t}}}return true},forEach:("forEach" in c)?function(u,t,e){return u.forEach(t,e)}:function(w,u,t){for(var e=0,v=w.length;e=0&&t>>0,t=e;if(arguments.length<3){while(true){if(u in x){t=x[u++];break}if(++u>=v){throw new TypeError("Reduce of empty array with no initial value")}}}for(;ue){e=u}}}return e},mean:function(e){return e.length>0?a.sum(e)/e.length:undefined},sum:function(w){var t=0,e,v,u;for(e=0,v=w.length;et?1:-1,e;for(e=t;e!=v;e+=w){x[e]=x[e+w]}x[v]=u},replace:p,splice:q,push:function(v){var e=arguments.length,u=1,t;if(v===undefined){v=[]}else{if(!Ext.isArray(v)){v=[v]}}for(;u=o){p=0}else{p=o-p}}if(p===0){q=r+q}else{if(p>=q.length){q+=r}else{q=q.substr(0,p)+r+q.substr(p)}}return q},startsWith:function(q,r,p){var o=c(q,r);if(o){if(p){q=q.toLowerCase();r=r.toLowerCase()}o=q.lastIndexOf(r,0)===0}return o},endsWith:function(r,p,q){var o=c(r,p);if(o){if(q){r=r.toLowerCase();p=p.toLowerCase()}o=r.indexOf(p,r.length-p.length)!==-1}return o},createVarName:function(o){return o.replace(k,"")},htmlEncode:function(o){return(!o)?o:String(o).replace(g,f)},htmlDecode:function(o){return(!o)?o:String(o).replace(d,j)},hasHtmlCharacters:function(o){return g.test(o)},addCharacterEntities:function(p){var o=[],s=[],q,r;for(q in p){r=p[q];a[q]=r;e[r]=q;o.push(r);s.push(q)}g=new RegExp("("+o.join("|")+")","g");d=new RegExp("("+s.join("|")+"|&#[0-9]{1,5};)","g")},resetCharacterEntities:function(){e={};a={};this.addCharacterEntities({"&":"&",">":">","<":"<",""":'"',"'":"'"})},urlAppend:function(p,o){if(!Ext.isEmpty(o)){return p+(p.indexOf("?")===-1?"?":"&")+o}return p},trim:function(o){if(o){o=o.replace(h,"")}return o||""},capitalize:function(o){if(o){o=o.charAt(0).toUpperCase()+o.substr(1)}return o||""},uncapitalize:function(o){if(o){o=o.charAt(0).toLowerCase()+o.substr(1)}return o||""},ellipsis:function(q,p,r){if(q&&q.length>p){if(r){var s=q.substr(0,p-2),o=Math.max(s.lastIndexOf(" "),s.lastIndexOf("."),s.lastIndexOf("!"),s.lastIndexOf("?"));if(o!==-1&&o>=(p-15)){return s.substr(0,o)+"..."}}return q.substr(0,p-3)+"..."}return q},escapeRegex:function(o){return o.replace(b,"\\$1")},createRegex:function(s,r,p,o){var q=s;if(s!=null&&!s.exec){q=m.escapeRegex(String(s));if(r!==false){q="^"+q}if(p!==false){q+="$"}q=new RegExp(q,(o!==false)?"i":"")}return q},escape:function(o){return o.replace(l,"\\$1")},toggle:function(p,q,o){return p===q?o:q},leftPad:function(p,q,r){var o=String(p);r=r||" ";while(o.length daysInMonth) {","d = daysInMonth;","}","}","h = from(h, from(def.h, dt.getHours()));","i = from(i, from(def.i, dt.getMinutes()));","s = from(s, from(def.s, dt.getSeconds()));","ms = from(ms, from(def.ms, dt.getMilliseconds()));","if(z >= 0 && y >= 0){","v = me.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), me.YEAR, y < 100 ? y - 100 : 0);","v = !strict? v : (strict === true && (z <= 364 || (me.isLeapYear(v) && z <= 365))? me.add(v, me.DAY, z) : null);","}else if(strict === true && !me.isValid(y, m + 1, d, h, i, s, ms)){","v = null;","}else{","if (W) {","year = y || (new Date()).getFullYear();","jan4 = new Date(year, 0, 4, 0, 0, 0);","d = jan4.getDay();","week1monday = new Date(jan4.getTime() - ((d === 0 ? 6 : d - 1) * 86400000));","v = Ext.Date.clearTime(new Date(week1monday.getTime() + ((W - 1) * 604800000 + 43200000)));","} else {","v = me.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), me.YEAR, y < 100 ? y - 100 : 0);","}","}","}","}","if(v){","if(zz != null){","v = me.add(v, me.SECOND, -v.getTimezoneOffset() * 60 - zz);","}else if(o){","v = me.add(v, me.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));","}","}","return (v != null) ? v : null;"].join("\n");if(!Date.prototype.toISOString){Date.prototype.toISOString=function(){var l=this;return d(l.getUTCFullYear(),4,"0")+"-"+d(l.getUTCMonth()+1,2,"0")+"-"+d(l.getUTCDate(),2,"0")+"T"+d(l.getUTCHours(),2,"0")+":"+d(l.getUTCMinutes(),2,"0")+":"+d(l.getUTCSeconds(),2,"0")+"."+d(l.getUTCMilliseconds(),3,"0")+"Z"}}function i(m){var l=Array.prototype.slice.call(arguments,1);return m.replace(c,function(n,o){return l[o]})}return f={now:e.now,toString:function(l){if(!l){l=new e()}return l.getFullYear()+"-"+d(l.getMonth()+1,2,"0")+"-"+d(l.getDate(),2,"0")+"T"+d(l.getHours(),2,"0")+":"+d(l.getMinutes(),2,"0")+":"+d(l.getSeconds(),2,"0")},getElapsed:function(m,l){return Math.abs(m-(l||f.now()))},useStrict:false,formatCodeToRegex:function(m,l){var n=f.parseCodes[m];if(n){n=typeof n==="function"?n():n;f.parseCodes[m]=n}return n?Ext.applyIf({c:n.c?i(n.c,l||"{0}"):n.c},n):{g:0,c:null,s:Ext.String.escapeRegex(m)}},parseFunctions:{MS:function(m,l){var n=(m||"").match(h);return n?new e(((n[1]||"")+n[2])*1):null},time:function(m,l){var n=parseInt(m,10);if(n||n===0){return new e(n)}return null},timestamp:function(m,l){var n=parseInt(m,10);if(n||n===0){return new e(n*1000)}return null}},parseRegexes:[],formatFunctions:{MS:function(){return"\\/Date("+this.getTime()+")\\/"},time:function(){return this.getTime().toString()},timestamp:function(){return f.format(this,"U")}},y2kYear:50,MILLI:"ms",SECOND:"s",MINUTE:"mi",HOUR:"h",DAY:"d",MONTH:"mo",YEAR:"y",defaults:{},dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNumbers:{January:0,Jan:0,February:1,Feb:1,March:2,Mar:2,April:3,Apr:3,May:4,June:5,Jun:5,July:6,Jul:6,August:7,Aug:7,September:8,Sep:8,October:9,Oct:9,November:10,Nov:10,December:11,Dec:11},defaultFormat:"m/d/Y",getShortMonthName:function(l){return f.monthNames[l].substring(0,3)},getShortDayName:function(l){return f.dayNames[l].substring(0,3)},getMonthNumber:function(l){return f.monthNumbers[l.substring(0,1).toUpperCase()+l.substring(1,3).toLowerCase()]},formatContainsHourInfo:function(l){return a.test(l.replace(k,""))},formatContainsDateInfo:function(l){return g.test(l.replace(k,""))},unescapeFormat:function(l){return l.replace(j,"")},formatCodes:{d:"Ext.String.leftPad(m.getDate(), 2, '0')",D:"Ext.Date.getShortDayName(m.getDay())",j:"m.getDate()",l:"Ext.Date.dayNames[m.getDay()]",N:"(m.getDay() ? m.getDay() : 7)",S:"Ext.Date.getSuffix(m)",w:"m.getDay()",z:"Ext.Date.getDayOfYear(m)",W:"Ext.String.leftPad(Ext.Date.getWeekOfYear(m), 2, '0')",F:"Ext.Date.monthNames[m.getMonth()]",m:"Ext.String.leftPad(m.getMonth() + 1, 2, '0')",M:"Ext.Date.getShortMonthName(m.getMonth())",n:"(m.getMonth() + 1)",t:"Ext.Date.getDaysInMonth(m)",L:"(Ext.Date.isLeapYear(m) ? 1 : 0)",o:"(m.getFullYear() + (Ext.Date.getWeekOfYear(m) == 1 && m.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(m) >= 52 && m.getMonth() < 11 ? -1 : 0)))",Y:"Ext.String.leftPad(m.getFullYear(), 4, '0')",y:"('' + m.getFullYear()).substring(2, 4)",a:"(m.getHours() < 12 ? 'am' : 'pm')",A:"(m.getHours() < 12 ? 'AM' : 'PM')",g:"((m.getHours() % 12) ? m.getHours() % 12 : 12)",G:"m.getHours()",h:"Ext.String.leftPad((m.getHours() % 12) ? m.getHours() % 12 : 12, 2, '0')",H:"Ext.String.leftPad(m.getHours(), 2, '0')",i:"Ext.String.leftPad(m.getMinutes(), 2, '0')",s:"Ext.String.leftPad(m.getSeconds(), 2, '0')",u:"Ext.String.leftPad(m.getMilliseconds(), 3, '0')",O:"Ext.Date.getGMTOffset(m)",P:"Ext.Date.getGMTOffset(m, true)",T:"Ext.Date.getTimezone(m)",Z:"(m.getTimezoneOffset() * -60)",c:function(){var q="Y-m-dTH:i:sP",o=[],n,m=q.length,p;for(n=0;n me.y2kYear ? 1900 + ty : 2000 + ty;\n",s:"(\\d{2})"},a:{g:1,c:"if (/(am)/i.test(results[{0}])) {\nif (!h || h == 12) { h = 0; }\n} else { if (!h || h < 12) { h = (h || 0) + 12; }}",s:"(am|pm|AM|PM)",calcAtEnd:true},A:{g:1,c:"if (/(am)/i.test(results[{0}])) {\nif (!h || h == 12) { h = 0; }\n} else { if (!h || h < 12) { h = (h || 0) + 12; }}",s:"(AM|PM|am|pm)",calcAtEnd:true},g:{g:1,c:"h = parseInt(results[{0}], 10);\n",s:"(1[0-2]|[0-9])"},G:{g:1,c:"h = parseInt(results[{0}], 10);\n",s:"(2[0-3]|1[0-9]|[0-9])"},h:{g:1,c:"h = parseInt(results[{0}], 10);\n",s:"(1[0-2]|0[1-9])"},H:{g:1,c:"h = parseInt(results[{0}], 10);\n",s:"(2[0-3]|[0-1][0-9])"},i:{g:1,c:"i = parseInt(results[{0}], 10);\n",s:"([0-5][0-9])"},s:{g:1,c:"s = parseInt(results[{0}], 10);\n",s:"([0-5][0-9])"},u:{g:1,c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",s:"(\\d+)"},O:{g:1,c:["o = results[{0}];","var sn = o.substring(0,1),","hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),","mn = o.substring(3,5) % 60;","o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n"].join("\n"),s:"([+-]\\d{4})"},P:{g:1,c:["o = results[{0}];","var sn = o.substring(0,1),","hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),","mn = o.substring(4,6) % 60;","o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n"].join("\n"),s:"([+-]\\d{2}:\\d{2})"},T:{g:0,c:null,s:"[A-Z]{1,5}"},Z:{g:1,c:"zz = results[{0}] * 1;\nzz = (-43200 <= zz && zz <= 50400)? zz : null;\n",s:"([+-]?\\d{1,5})"},c:function(){var o=[],m=[f.formatCodeToRegex("Y",1),f.formatCodeToRegex("m",2),f.formatCodeToRegex("d",3),f.formatCodeToRegex("H",4),f.formatCodeToRegex("i",5),f.formatCodeToRegex("s",6),{c:"ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"},{c:["if(results[8]) {","if(results[8] == 'Z'){","zz = 0;","}else if (results[8].indexOf(':') > -1){",f.formatCodeToRegex("P",8).c,"}else{",f.formatCodeToRegex("O",8).c,"}","}"].join("\n")}],p,n;for(p=0,n=m.length;p0?"-":"+")+Ext.String.leftPad(Math.floor(Math.abs(n)/60),2,"0")+(m?":":"")+Ext.String.leftPad(Math.abs(n%60),2,"0")},getDayOfYear:function(o){var n=0,q=f.clone(o),l=o.getMonth(),p;for(p=0,q.setDate(1),q.setMonth(0);p28){l=Math.min(l,f.getLastDateOfMonth(f.add(f.getFirstDateOfMonth(n),f.MONTH,q)).getDate())}r.setDate(l);r.setMonth(n.getMonth()+q);break;case f.YEAR:l=n.getDate();if(l>28){l=Math.min(l,f.getLastDateOfMonth(f.add(f.getFirstDateOfMonth(n),f.YEAR,q)).getDate())}r.setDate(l);r.setFullYear(n.getFullYear()+q);break}}if(p){switch(m.toLowerCase()){case f.MILLI:o=1;break;case f.SECOND:o=1000;break;case f.MINUTE:o=1000*60;break;case f.HOUR:o=1000*60*60;break;case f.DAY:o=1000*60*60*24;break;case f.MONTH:l=f.getDaysInMonth(r);o=1000*60*60*24*l;break;case f.YEAR:l=(f.isLeapYear(r)?366:365);o=1000*60*60*24*l;break}if(o){r.setTime(r.getTime()+o*p)}}return r},subtract:function(m,l,n){return f.add(m,l,-n)},between:function(m,o,l){var n=m.getTime();return o.getTime()<=n&&n<=l.getTime()},compat:function(){var t,u=["useStrict","formatCodeToRegex","parseFunctions","parseRegexes","formatFunctions","y2kYear","MILLI","SECOND","MINUTE","HOUR","DAY","MONTH","YEAR","defaults","dayNames","monthNames","monthNumbers","getShortMonthName","getShortDayName","getMonthNumber","formatCodes","isValid","parseDate","getFormatCode","createFormat","createParser","parseCodes"],r=["dateFormat","format","getTimezone","getGMTOffset","getDayOfYear","getWeekOfYear","isLeapYear","getFirstDayOfMonth","getLastDayOfMonth","getDaysInMonth","getSuffix","clone","isDST","clearTime","add","between"],m=u.length,l=r.length,o,q,n;for(n=0;nl){return n-1}return n;case f.YEAR:n=l.getFullYear()-m.getFullYear();if(f.add(m,o,n)>l){return n-1}else{return n}}},align:function(m,o,n){var l=new e(+m);switch(o.toLowerCase()){case f.MILLI:return l;case f.SECOND:l.setUTCSeconds(l.getUTCSeconds()-l.getUTCSeconds()%n);l.setUTCMilliseconds(0);return l;case f.MINUTE:l.setUTCMinutes(l.getUTCMinutes()-l.getUTCMinutes()%n);l.setUTCSeconds(0);l.setUTCMilliseconds(0);return l;case f.HOUR:l.setUTCHours(l.getUTCHours()-l.getUTCHours()%n);l.setUTCMinutes(0);l.setUTCSeconds(0);l.setUTCMilliseconds(0);return l;case f.DAY:if(n===7||n===14){l.setUTCDate(l.getUTCDate()-l.getUTCDay()+1)}l.setUTCHours(0);l.setUTCMinutes(0);l.setUTCSeconds(0);l.setUTCMilliseconds(0);return l;case f.MONTH:l.setUTCMonth(l.getUTCMonth()-(l.getUTCMonth()-1)%n,1);l.setUTCHours(0);l.setUTCMinutes(0);l.setUTCSeconds(0);l.setUTCMilliseconds(0);return l;case f.YEAR:l.setUTCFullYear(l.getUTCFullYear()-l.getUTCFullYear()%n,1,1);l.setUTCHours(0);l.setUTCMinutes(0);l.setUTCSeconds(0);l.setUTCMilliseconds(0);return m}}}}());Ext.Function=(function(){var b=0,l,e=[],m=[],h=0,i={},g=window,d=Ext.global,f=!!(d.setImmediate&&d.clearImmediate),k=g.requestAnimationFrame||g.webkitRequestAnimationFrame||g.mozRequestAnimationFrame||g.oRequestAnimationFrame||function(q){var n=Ext.now(),o=Math.max(0,16-(n-b)),p=g.setTimeout(function(){q(n+o)},o);b=n+o;return p},c=function(){var n=e.length,q,o,p;l=null;for(o=0;o0){return setTimeout(function(){if(Ext.elevateFunction){Ext.elevateFunction(r)}else{r()}},p)}r();return 0},interval:function(r,p,q,o,n){r=Ext.Function.bind(r,q,o,n);return setInterval(function(){if(Ext.elevateFunction){Ext.elevateFunction(r)}else{r()}},p)},createSequence:function(o,p,n){if(!p){return o}else{return function(){var q=o.apply(this,arguments);p.apply(n||this,arguments);return q}}},createBuffered:function(r,o,q,p){var n;return function(){var t=p||Array.prototype.slice.call(arguments,0),s=q||this;if(n){clearTimeout(n)}n=setTimeout(function(){if(Ext.elevateFunction){Ext.elevateFunction(r,s,t)}else{r.apply(s,t)}},o)}},createAnimationFrame:function(q,p,o,r){var n;r=r||3;return function(){var s=o||Array.prototype.slice.call(arguments,0);p=p||this;if(r===3&&n){j.cancelAnimationFrame(n)}if((r&1)||!n){n=j.requestAnimationFrame(function(){n=null;q.apply(p,s)})}}},requestAnimationFrame:function(q,p,n){var r=++h,o=Array.prototype.slice.call(arguments,0);o[3]=r;i[r]=1;e.push(o);if(!l){l=k(Ext.elevateFunction?a:c)}return r},cancelAnimationFrame:function(n){delete i[n]},createThrottled:function(r,o,q){var s=0,n,p,u,t=function(){if(Ext.elevateFunction){Ext.elevateFunction(r,q,p)}else{r.apply(q,p)}s=Ext.now();u=null};return function(){if(!q){q=this}n=Ext.now()-s;p=arguments;if(n>=o){clearTimeout(u);t()}else{if(!u){u=Ext.defer(t,o-n)}}}},createBarrier:function(p,o,n){return function(){if(!--p){o.apply(n,arguments)}}},interceptBefore:function(o,n,q,p){var r=o[n]||Ext.emptyFn;return(o[n]=function(){var s=q.apply(p||this,arguments);r.apply(this,arguments);return s})},interceptAfter:function(o,n,q,p){var r=o[n]||Ext.emptyFn;return(o[n]=function(){r.apply(this,arguments);return q.apply(p||this,arguments)})},makeCallback:function(o,n){if(!n[o]){if(n.$className){Ext.raise('No method "'+o+'" on '+n.$className)}Ext.raise('No method "'+o+'"')}return function(){return n[o].apply(n,arguments)}},memoize:function(q,p,n){var o={},r=n&&Ext.isFunction(n);return function(t){var s=r?n.apply(p,arguments):t;if(!(s in o)){o[s]=q.apply(p,arguments)}return o[s]}}};Ext.asap=f?function(o,n,p){if(n!=null||p!=null){o=j.bind(o,n,p)}return setImmediate(function(){if(Ext.elevateFunction){Ext.elevateFunction(o)}else{o()}})}:function(o,n,p){if(n!=null||p!=null){o=j.bind(o,n,p)}return setTimeout(function(){if(Ext.elevateFunction){Ext.elevateFunction(o)}else{o()}},0,true)},Ext.asapCancel=f?function(n){clearImmediate(n)}:function(n){clearTimeout(n)};Ext.defer=j.defer;Ext.interval=j.interval;Ext.pass=j.pass;Ext.bind=j.bind;Ext.deferCallback=j.requestAnimationFrame;return j})();Ext.Number=(new function(){var d=this,c=(0.9).toFixed()!=="1",b=Math,a={count:false,inclusive:false,wrap:true};Ext.apply(d,{Clip:{DEFAULT:a,COUNT:Ext.applyIf({count:true},a),INCLUSIVE:Ext.applyIf({inclusive:true},a),NOWRAP:Ext.applyIf({wrap:false},a)},clipIndices:function(l,m,g){g=g||a;var f=0,k=g.wrap,j,e,h;m=m||[];for(h=0;h<2;++h){j=e;e=m[h];if(e==null){e=f}else{if(h&&g.count){e+=j;e=(e>l)?l:e}else{if(k){e=(e<0)?(l+e):e}if(h&&g.inclusive){++e}e=(e<0)?0:((e>l)?l:e)}}f=l}m[0]=j;m[1]=(ef)?f:e)},snap:function(h,f,g,i){var e;if(h===undefined||h=f){h+=f}else{if(e*2<-f){h-=f}}}}return d.constrain(h,g,i)},snapInRange:function(h,e,g,i){var f;g=(g||0);if(h===undefined||h=e){h+=e}}if(i!==undefined){if(h>(i=d.snapInRange(i,e,g))){h=i}}return h},sign:function(e){e=+e;if(e===0||isNaN(e)){return e}return(e>0)?1:-1},toFixed:c?function(g,e){e=e||0;var f=b.pow(10,e);return(b.round(g*f)/f).toFixed(e)}:function(f,e){return f.toFixed(e)},from:function(f,e){if(isFinite(f)){f=parseFloat(f)}return !isNaN(f)?f:e},randomInt:function(f,e){return b.floor(b.random()*(e-f+1)+f)},correctFloat:function(e){return parseFloat(e.toPrecision(14))}});Ext.num=function(){return d.from.apply(this,arguments)}}());(function(){var d=function(){},b=/^\?/,c=/(\[):?([^\]]*)\]/g,a=/^([^\[]+)/,f=/\+/g,e=Ext.Object={chain:Object.create||function(h){d.prototype=h;var g=new d();d.prototype=null;return g},clear:function(g){for(var h in g){delete g[h]}return g},freeze:Object.freeze?function(i,g){if(i&&typeof i==="object"&&!Object.isFrozen(i)){Object.freeze(i);if(g){for(var h in i){e.freeze(i[h],g)}}}return i}:Ext.identityFn,toQueryObjects:function(j,n,h){var g=e.toQueryObjects,m=[],k,l;if(Ext.isArray(n)){for(k=0,l=n.length;k0){n=r.split("=");z=n[0];z=z.replace(f,"%20");z=decodeURIComponent(z);q=n[1];if(q!==undefined){q=q.replace(f,"%20");q=decodeURIComponent(q)}else{q=""}if(!u){if(x.hasOwnProperty(z)){if(!Ext.isArray(x[z])){x[z]=[x[z]]}x[z].push(q)}else{x[z]=q}}else{m=z.match(c);w=z.match(a);if(!w){throw new Error('[Ext.Object.fromQueryString] Malformed query string given, failed parsing name from "'+r+'"')}z=w[0];o=[];if(m===null){x[z]=q;continue}for(s=0,g=m.length;s@:]*)(?:[@]([^<>@:]+))?[>](.+)$/,resolveResource:function(c){var b=c,a;if(c&&c.charAt(0)==="<"){a=Ext._resourcePoolRe.exec(c);if(a){b=Ext.getResourcePath(a[3],a[1],a[2])}}return b},urlEncode:function(){var a=Ext.Array.from(arguments),b="";if(Ext.isString(a[1])){b=a[1]+"&";a[1]=false}return b+Ext.Object.toQueryString.apply(Ext.Object,a)},urlDecode:function(){return Ext.Object.fromQueryString.apply(Ext.Object,arguments)},getScrollbarSize:function(c){if(!Ext.isDomReady){Ext.raise("getScrollbarSize called before DomReady")}var b=Ext._scrollbarSize;if(c||!b){var a=document.body,d=document.createElement("div");d.style.width=d.style.height="100px";d.style.overflow="scroll";d.style.position="absolute";a.appendChild(d);Ext._scrollbarSize=b={width:d.offsetWidth-d.clientWidth,height:d.offsetHeight-d.clientHeight};a.removeChild(d)}return b},typeOf:(function(){var a=/\S/,c=Object.prototype.toString,d={number:1,string:1,"boolean":1,"undefined":1},b={"[object Array]":"array","[object Date]":"date","[object Boolean]":"boolean","[object Number]":"number","[object RegExp]":"regexp"};return function(g){if(g===null){return"null"}var f=typeof g,e,h;if(d[f]){return f}e=b[h=c.call(g)];if(e){return e}if(f==="function"){return"function"}if(f==="object"){if(g.nodeType!==undefined){if(g.nodeType===3){return a.test(g.nodeValue)?"textnode":"whitespace"}else{return"element"}}return"object"}Ext.raise({sourceClass:"Ext",sourceMethod:"typeOf",msg:'Failed to determine the type of "'+g+'".'});return h}}()),factory:function(b,e,a,f){var d=Ext.ClassManager,c;if(!b||b.isInstance){if(a&&a!==b){a.destroy()}return b}if(f){if(typeof b==="string"){return d.instantiateByAlias(f+"."+b)}else{if(Ext.isObject(b)&&"type" in b){return d.instantiateByAlias(f+"."+b.type,b)}}}if(b===true){if(!a&&!e){Ext.raise("[Ext.factory] Cannot determine type of class to create")}return a||Ext.create(e)}if(!Ext.isObject(b)){Ext.raise("Invalid config, must be a valid config object")}if("xtype" in b){c=d.instantiateByAlias("widget."+b.xtype,b)}else{if("xclass" in b){c=Ext.create(b.xclass,b)}}if(c){if(a){a.destroy()}return c}if(a){return a.setConfig(b)}return Ext.create(e,b)},log:(function(){var c=/string|number|boolean/;function a(g,e,o,m){var i,l,n,f,j,p,h=[];if(Ext.isArray(g)){j="[";p="]"}else{if(Ext.isObject(g)){j="{";p="}"}}if(!o){o=3}if(e>o){return j+"..."+p}e=e||1;var k=(new Array(e)).join(" ");for(f in g){if(g.hasOwnProperty(f)){n=g[f];l=typeof n;if(l==="function"){if(!m){continue}i=l}else{if(l==="undefined"){i=l}else{if(n===null||c.test(l)||Ext.isDate(n)){i=Ext.encode(n)}else{if(Ext.isArray(n)){i=a(n,e+1,o,m)}else{if(Ext.isObject(n)){i=a(n,e+1,o,m)}else{i=l}}}}}h.push(k+f+": "+i)}}if(h.length){return j+"\n "+h.join(",\n ")+"\n"+k+p}return j+p}function b(n){var o,j,f=Ext.global.console,e="log",g=b.indent||0,i,m,l,h,k;b.indent=g;if(typeof n!=="string"){o=n;n=o.msg||"";e=o.level||e;j=o.dump;m=o.stack;i=o.prefix;l=o.fn;if(o.indent){++b.indent}else{if(o.outdent){b.indent=g=Math.max(g-1,0)}}if(j&&!(f&&f.dir)){n+=a(j);j=null}}if(arguments.length>1){n+=Array.prototype.slice.call(arguments,1).join("")}if(i){n=i+" - "+n}n=g?Ext.String.repeat(" ",b.indentSize*g)+n:n;if(e!=="log"){n="["+e.charAt(0).toUpperCase()+"] "+n}if(l){n+="\nCaller: "+l.toString()}if(f){if(f[e]){f[e](n)}else{f.log(n)}if(j){f.dir(j)}if(m&&f.trace){if(!f.firebug||e!=="error"){f.trace()}}}else{if(Ext.isOpera){opera.postError(n)}else{h=b.out;k=b.max;if(h.length>=k){Ext.Array.erase(h,0,h.length-3*Math.floor(k/4))}h.push(n)}}++b.count;++b.counters[e]}function d(f,e){if(typeof e[0]==="string"){e.unshift({})}e[0].level=f;b.apply(this,e)}b.error=function(){d("error",Array.prototype.slice.call(arguments))};b.info=function(){d("info",Array.prototype.slice.call(arguments))};b.warn=function(){d("warn",Array.prototype.slice.call(arguments))};b.count=0;b.counters={error:0,warn:0,info:0,log:0};b.indentSize=2;b.out=[];b.max=750;return b}())||(function(){var a=function(){};a.info=a.warn=a.error=Ext.emptyFn;return a}())});(function(){var d=[""],h=/([^\d\.])/,b=/[^\d]/g,a=/[\-+]/g,g=/\s/g,c=/_/g,f={classic:1,modern:1},e;Ext.Version=e=function(r,n){var s=this,l=s.padModes,j,p,m,o,t,k,q;if(r.isVersion){r=r.version}s.version=q=String(r).toLowerCase().replace(c,".").replace(a,"");j=q.charAt(0);if(j in l){q=q.substring(1);m=l[j]}else{m=n?l[n]:0}s.pad=m;k=q.search(h);s.shortVersion=q;if(k!==-1){s.release=t=q.substr(k,r.length);s.shortVersion=q.substr(0,k);t=e.releaseValueMap[t]||t}s.releaseValue=t||m;s.shortVersion=s.shortVersion.replace(b,"");s.parts=o=q.split(".");for(p=o.length;p--;){o[p]=parseInt(o[p],10)}if(m===Infinity){o.push(m)}s.major=o[0]||m;s.minor=o[1]||m;s.patch=o[2]||m;s.build=o[3]||m;return s};e.prototype={isVersion:true,padModes:{"~":NaN,"^":Infinity},release:"",compareTo:function(t){var u=this,n=u.pad,r=u.parts,v=r.length,m=t.isVersion?t:new e(t),k=m.pad,q=m.parts,p=q.length,j=Math.max(v,p),o,l,s;for(o=0;os){return 1}}l=u.releaseValue;s=m.releaseValue;if(ls){return 1}return 0},toString:function(){return this.version},valueOf:function(){return this.version},getMajor:function(){return this.major},getMinor:function(){return this.minor},getPatch:function(){return this.patch},getBuild:function(){return this.build},getRelease:function(){return this.release},getReleaseValue:function(){return this.releaseValue},isGreaterThan:function(i){return this.compareTo(i)>0},isGreaterThanOrEqual:function(i){return this.compareTo(i)>=0},isLessThan:function(i){return this.compareTo(i)<0},isLessThanOrEqual:function(i){return this.compareTo(i)<=0},equals:function(i){return this.compareTo(i)===0},match:function(i){i=String(i);return this.version.substr(0,i.length)===i},toArray:function(){var i=this;return[i.getMajor(),i.getMinor(),i.getPatch(),i.getBuild(),i.getRelease()]},getShortVersion:function(){return this.shortVersion},gt:function(i){return this.compareTo(i)>0},lt:function(i){return this.compareTo(i)<0},gtEq:function(i){return this.compareTo(i)>=0},ltEq:function(i){return this.compareTo(i)<=0}};Ext.apply(e,{aliases:{from:{extjs:"ext",core:"core",touch:"modern"},to:{ext:["extjs"],core:["core"],modern:["touch"]}},releaseValueMap:{dev:-6,alpha:-5,a:-5,beta:-4,b:-4,rc:-3,"#":-2,p:-1,pl:-1},getComponentValue:function(i){return !i?0:(isNaN(i)?this.releaseValueMap[i]||i:parseInt(i,10))},compare:function(k,j){var i=k.isVersion?k:new e(k);return i.compareTo(j)},set:function(o,m,l){var k=e.aliases.to[m],j=l.isVersion?l:new e(l),n;o[m]=j;if(k){for(n=k.length;n-->0;){o[k[n]]=j}}return j}});Ext.apply(Ext,{compatVersions:{},versions:{},lastRegisteredVersion:null,getCompatVersion:function(j){var i=Ext.compatVersions,k;if(!j){k=i.ext||i.touch||i.core}else{k=i[e.aliases.from[j]||j]}return k||Ext.getVersion(j)},setCompatVersion:function(j,i){e.set(Ext.compatVersions,j,i)},setVersion:function(j,i){if(j in f){Ext.toolkit=j}Ext.lastRegisteredVersion=e.set(Ext.versions,j,i);return this},getVersion:function(j){var i=Ext.versions;if(!j){return i.ext||i.touch||i.core}return i[e.aliases.from[j]||j]},checkVersion:function(p,x){var t=Ext.isArray(p),l=e.aliases.from,y=t?p:d,k=y.length,m=Ext.versions,w=m.ext||m.touch,q,v,s,n,o,j,z,r,u;if(!t){d[0]=p}for(q=0;q=0){z=z.replace(g,"")}v=z.indexOf("@");if(v<0){r=z;u=w}else{j=z.substring(0,v);if(!(u=m[l[j]||j])){if(x){return false}continue}r=z.substring(v+1)}v=r.indexOf("-");if(v<0){if(r.charAt(v=r.length-1)==="+"){n=r.substring(0,v);o=null}else{n=o=r}}else{if(v>0){n=r.substring(0,v);o=r.substring(v+1)}else{n=null;o=r.substring(v+1)}}s=true;if(n){n=new e(n,"~");s=n.ltEq(u)}if(s&&o){o=new e(o,"~");s=o.gtEq(u)}}if(s){if(!x){return true}}else{if(x){return false}}}return !!x},deprecate:function(i,k,l,j){if(e.compare(Ext.getVersion(i),k)<1){l.call(j)}}})}());(function(d){var e=(d&&d.packages)||{},c=d&&d.compatibility,b,a;for(b in e){a=e[b];Ext.setVersion(b,a.version)}if(c){if(Ext.isString(c)){Ext.setCompatVersion("core",c)}else{for(b in c){Ext.setCompatVersion(b,c[b])}}}if(!e.ext&&!e.touch){Ext.setVersion("ext","6.0.1.250");Ext.setVersion("core","6.0.1.250")}})(Ext.manifest);Ext.Config=function(b){var c=this,a=b.charAt(0).toUpperCase()+b.substr(1);c.name=b;c.names={internal:"_"+b,initializing:"is"+a+"Initializing",apply:"apply"+a,update:"update"+a,get:"get"+a,set:"set"+a,initGet:"initGet"+a,changeEvent:b.toLowerCase()+"change"};c.root=c};Ext.Config.map={};Ext.Config.get=function(b){var c=Ext.Config.map,a=c[b]||(c[b]=new Ext.Config(b));return a};Ext.Config.prototype={self:Ext.Config,isConfig:true,getGetter:function(){return this.getter||(this.root.getter=this.makeGetter())},getInitGetter:function(){return this.initGetter||(this.root.initGetter=this.makeInitGetter())},getSetter:function(){return this.setter||(this.root.setter=this.makeSetter())},getEventedSetter:function(){return this.eventedSetter||(this.root.eventedSetter=this.makeEventedSetter())},getInternalName:function(a){return a.$configPrefixed?this.names.internal:this.name},mergeNew:function(f,b,e,d){var a,c;if(!b){a=f}else{if(!f){a=b}else{a=Ext.Object.chain(b);for(c in f){if(!d||!(c in a)){a[c]=f[c]}}}}return a},mergeSets:function(e,c,a){var b=c?Ext.Object.chain(c):{},d,f;if(e instanceof Array){for(d=e.length;d--;){f=e[d];if(!a||!(f in b)){b[f]=true}}}else{if(e){if(e.constructor===Object){for(d in e){f=e[d];if(!a||!(d in b)){b[d]=f}}}else{if(!a||!(e in b)){b[e]=true}}}}return b},makeGetter:function(){var a=this.name,b=this.names.internal;return function(){var c=this.$configPrefixed?b:a;return this[c]}},makeInitGetter:function(){var a=this.name,e=this.names,d=e.set,b=e.get,c=e.initializing;return function(){var f=this;f[c]=true;delete f[b];f[d](f.config[a]);delete f[c];return f[b].apply(f,arguments)}},makeSetter:function(){var a=this.name,e=this.names,c=e.internal,d=e.get,b=e.apply,g=e.update,f;f=function(k){var j=this,i=j.$configPrefixed?c:a,h=j[i];delete j[d];if(!j[b]||(k=j[b](k,h))!==undefined){if(k!==(h=j[i])){j[i]=k;if(j[g]){j[g](k,h)}}}return j};f.$isDefault=true;return f},makeEventedSetter:function(){var b=this.name,g=this.names,i=g.internal,a=g.get,h=g.apply,d=g.update,f=g.changeEvent,e=function(l,m,j,k){l[k]=m;if(l[d]){l[d](m,j)}},c;c=function(m){var l=this,k=l.$configPrefixed?i:b,j=l[k];delete l[a];if(!l[h]||(m=l[h](m,j))!==undefined){if(m!==(j=l[k])){if(l.isConfiguring){l[k]=m;if(l[d]){l[d](m,j)}}else{l.fireEventedAction(f,[l,m,j],e,l,[l,m,j,k])}}}return l};c.$isDefault=true;return c}};(function(){var b=Ext.Config,c=b.map,a=Ext.Object;Ext.Configurator=function(d){var f=this,e=d.prototype,g=d.superclass?d.superclass.self.$config:null;f.cls=d;f.superCfg=g;if(g){f.configs=a.chain(g.configs);f.cachedConfigs=a.chain(g.cachedConfigs);f.initMap=a.chain(g.initMap);f.values=a.chain(g.values);f.needsFork=g.needsFork;f.deprecations=a.chain(g.deprecations)}else{f.configs={};f.cachedConfigs={};f.initMap={};f.values={};f.deprecations={}}e.config=e.defaultConfig=f.values;d.$config=f};Ext.Configurator.prototype={self:Ext.Configurator,needsFork:false,initList:null,add:function(t,d){var u=this,h=u.cls,k=u.configs,v=u.cachedConfigs,m=u.initMap,p=h.prototype,w=d&&d.$config.configs,e=u.values,j,l,r,f,g,i,x,o,n,q;for(x in t){q=t[x];j=q&&q.constructor===Object;l=j&&"$value" in q?q:null;if(l){r=!!l.cached;q=l.$value;j=q&&q.constructor===Object}f=l&&l.merge;g=k[x];if(g){if(d){f=g.merge;if(!f){continue}l=null}else{f=f||g.merge}if(!d&&r&&!v[x]){Ext.raise("Redefining config as cached: "+x+" in class: "+h.$className)}i=e[x];if(f){q=f.call(g,q,i,h,d)}else{if(j){if(i&&i.constructor===Object){q=a.merge({},i,q)}}}}else{if(w){g=w[x];l=null}else{g=b.get(x)}k[x]=g;if(g.cached||r){v[x]=true}o=g.names;if(!p[n=o.get]){p[n]=g.getter||g.getGetter()}if(!p[n=o.set]){p[n]=(l&&l.evented)?(g.eventedSetter||g.getEventedSetter()):(g.setter||g.getSetter())}}if(l){if(g.owner!==h){k[x]=g=Ext.Object.chain(g);g.owner=h}Ext.apply(g,l);delete g.$value}if(!u.needsFork&&q&&(q.constructor===Object||q instanceof Array)){u.needsFork=true}if(q!==null){m[x]=true}else{if(p.$configPrefixed){p[k[x].names.internal]=null}else{p[k[x].name]=null}if(x in m){m[x]=false}}e[x]=q}},addDeprecations:function(j){var i=this,e=i.deprecations,g=(i.cls.$className||"")+"#",h,d,f;for(f in j){d=j[f];if(!d){h="This config has been removed."}else{if(!(h=d.message)){h='This config has been renamed to "'+d+'"'}}e[f]=g+f+": "+h}},configure:function(y,m){var A=this,l=A.configs,j=A.deprecations,n=A.initMap,p=A.initListMap,w=A.initList,q=A.cls.prototype,e=A.values,r=0,t=!w,f,g,h,C,v,u,k,o,B,s,z,x,d;e=A.needsFork?a.fork(e):a.chain(e);y.isConfiguring=true;if(t){A.initList=w=[];A.initListMap=p={};y.isFirstInstance=true;for(B in n){C=n[B];g=l[B];z=g.cached;if(C){o=g.names;s=e[B];if(!q[o.set].$isDefault||q[o.apply]||q[o.update]||typeof s==="object"){if(z){(f||(f=[])).push(g)}else{w.push(g);p[B]=true}y[o.get]=g.initGetter||g.getInitGetter()}else{q[g.getInternalName(q)]=s}}else{if(z){q[g.getInternalName(q)]=undefined}}}}k=f&&f.length;if(k){for(v=0;v0){for(o=0;of.maxSize){f.unlinkEntry(c.prev,true);--f.count}}return e.value},evict:Ext.emptyFn,linkEntry:function(d){var c=this.head,e=c.next;d.next=e;d.prev=c;c.next=d;e.prev=d},unlinkEntry:function(e,f){var c=e.next,d=e.prev;d.next=c;c.prev=d;if(f){this.evict(e.key,e.value)}}};a.destroy=a.clear}());(function(){var d,c=Ext.Base,e=c.$staticMembers,b=function(g,f){return(g.length-f.length)||((gf)?1:0))};function a(g){function f(){return this.constructor.apply(this,arguments)||null}if(g){f.name=g}return f}Ext.Class=d=function(g,h,f){if(typeof g!="function"){f=h;h=g;g=null}if(!h){h={}}g=d.create(g,h);d.process(g,h,f);return g};Ext.apply(d,{makeCtor:a,onBeforeCreated:function(g,h,f){Ext.classSystemMonitor&&Ext.classSystemMonitor(g,">> Ext.Class#onBeforeCreated",arguments);g.addMembers(h);f.onCreated.call(g,g);Ext.classSystemMonitor&&Ext.classSystemMonitor(g,"<< Ext.Class#onBeforeCreated",arguments)},create:function(f,j){var h=e.length,g;if(!f){f=a(j.$className)}while(h--){g=e[h];f[g]=c[g]}return f},process:function(f,n,h){var g=n.preprocessors||d.defaultPreprocessors,q=this.preprocessors,t={onBeforeCreated:this.onBeforeCreated},s=[],u,m,l,r,k,p,o;delete n.preprocessors;f._classHooks=t;for(l=0,r=g.length;l0){j=f.test(q[m])}q=d[v];if(q&&!j){m=q.length;while(!j&&m-->0){j=f.test(q[m])}}}if(j){t[v]=1;k.push(v)}}}}}return k},getPath:function(b){var c=this,e=c.paths,a="",d;if(b in e){a=e[b]}else{d=c.getPrefix(b);if(d){b=b.substring(d.length+1);a=e[d];if(a){a+="/"}}a+=b.replace(c.dotRe,"/")+".js"}return a},getPrefix:function(b){if(b in this.paths){return b}var e=this.getPrefixes(),a=e.length,c,d;while(a-->0){c=(d=e[a]).length;if(c0){z=g[t];u=v;v=z.value||v[z.name];if(!v&&y){u[z.name]=v={}}}return v},setNamespace:function(u,w){var v=c.getNamespaceEntry(u),t=Ext.global;if(v.parent){t=c.lookupName(v.parent,true)}t[v.name]=w;return w},setXType:function(t,A){var v=t.$className,z=v?t:c.get(v=t),w=z.prototype,x=w.xtypes,u=w.xtypesChain,y=w.xtypesMap;if(!w.hasOwnProperty("xtypes")){w.xtypes=x=[];w.xtypesChain=u=u?u.slice(0):[];w.xtypesMap=y=Ext.apply({},y)}c.addAlias(v,"widget."+A,true);x.push(A);u.push(A);y[A]=true},set:function(t,v){var u=c.getName(v);c.classes[t]=c.setNamespace(t,v);if(u&&u!==t){c.addAlternate(u,t)}return c},get:function(t){return c.classes[t]||c.lookupName(t,false)},addNameAliasMappings:function(t){c.addAlias(t)},addNameAlternateMappings:function(t){c.addAlternate(t)},getByAlias:function(t){return c.get(c.getNameByAlias(t))},getByConfig:function(u,v){var w=u.xclass,t;if(w){t=w}else{t=u.xtype;if(t){v="widget."}else{t=u.type}t=c.getNameByAlias(v+t)}return c.get(t)},getName:function(t){return t&&t.$className||""},getClass:function(t){return t&&t.self||null},create:function(u,w,t){if(u!=null&&typeof u!=="string"){throw new Error("[Ext.define] Invalid class name '"+u+"' specified, must be a non-empty string")}var v=b(u);if(typeof w==="function"){w=w(v)}if(u){if(c.classes[u]){Ext.log.warn("[Ext.define] Duplicate class name '"+u+"' specified, must be a non-empty string")}v.name=u}w.$className=u;return new p(v,w,function(){var x=w.postprocessors||c.defaultPostprocessors,E=c.postprocessors,F=[],D,z,C,y,B,A,G;delete w.postprocessors;for(z=0,C=x.length;z0;){t=c.lookupName(arguments[u],true)}return t}});Ext.addRootNamespaces=c.addRootNamespaces;Ext.createWidget=Ext.widget;Ext.ns=Ext.namespace;p.registerPreprocessor("className",function(t,u){if("$className" in u){t.$className=u.$className;t.displayName=t.$className}Ext.classSystemMonitor&&Ext.classSystemMonitor(t,"Ext.ClassManager#classNamePreprocessor",arguments)},true,"first");p.registerPreprocessor("alias",function(E,y){Ext.classSystemMonitor&&Ext.classSystemMonitor(E,"Ext.ClassManager#aliasPreprocessor",arguments);var C=E.prototype,v=e(y.xtype),t=e(y.alias),F="widget.",D=F.length,z=Array.prototype.slice.call(C.xtypesChain||[]),w=Ext.merge({},C.xtypesMap||{}),x,B,A,u;for(x=0,B=t.length;x=o){Ext[m+"p"]=true}}}if(u.is.Opera&&parseInt(d,10)<=12){Ext.isOpera12m=true}Ext.chromeVersion=Ext.isChrome?d:0;Ext.firefoxVersion=Ext.isFirefox?d:0;Ext.ieVersion=Ext.isIE?d:0;Ext.operaVersion=Ext.isOpera?d:0;Ext.safariVersion=Ext.isSafari?d:0;Ext.webKitVersion=Ext.isWebKit?d:0;this.setFlag(p+d,true,n);this.setFlag(p+l.getShortVersion())}for(o in c){if(c.hasOwnProperty(o)){v=c[o];this.setFlag(v,p===v)}}this.setFlag(v);if(f){this.setFlag(s+(f.getMajor()||""));this.setFlag(s+f.getShortVersion())}for(o in j){if(j.hasOwnProperty(o)){v=j[o];this.setFlag(v,s===v,n)}}this.setFlag("Standalone",!!navigator.standalone);this.setFlag("Ripple",!!document.getElementById("tinyhippos-injected")&&!Ext.isEmpty(window.top.ripple));this.setFlag("WebWorks",!!window.blackberry);if(window.PhoneGap!==undefined||window.Cordova!==undefined||window.cordova!==undefined){h=true;this.setFlag("PhoneGap");this.setFlag("Cordova")}if(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)(?!.*FBAN)/i.test(t)){h=true}this.setFlag("WebView",h);this.isStrict=Ext.isStrict=document.compatMode==="CSS1Compat";this.isSecure=Ext.isSecure;this.identity=p+d+(this.isStrict?"Strict":"Quirks")};Ext.env.Browser.prototype={constructor:Ext.env.Browser,engineNames:{webkit:"WebKit",gecko:"Gecko",presto:"Presto",trident:"Trident",other:"Other"},enginePrefixes:{webkit:"AppleWebKit/",gecko:"Gecko/",presto:"Presto/",trident:"Trident/"},styleDashPrefixes:{WebKit:"-webkit-",Gecko:"-moz-",Trident:"-ms-",Presto:"-o-",Other:""},stylePrefixes:{WebKit:"Webkit",Gecko:"Moz",Trident:"ms",Presto:"O",Other:""},propertyPrefixes:{WebKit:"webkit",Gecko:"moz",Trident:"ms",Presto:"o",Other:""},is:function(a){return !!this.is[a]},name:null,version:null,engineName:null,engineVersion:null,setFlag:function(a,c,b){if(c===undefined){c=true}this.is[a]=c;this.is[a.toLowerCase()]=c;if(b){Ext["is"+a]=c}return this},getStyleDashPrefix:function(){return this.styleDashPrefixes[this.engineName]},getStylePrefix:function(){return this.stylePrefixes[this.engineName]},getVendorProperyName:function(a){var b=this.propertyPrefixes[this.engineName];if(b.length>0){return b+Ext.String.capitalize(a)}return a},getPreferredTranslationMethod:function(a){if(typeof a==="object"&&"translationMethod" in a&&a.translationMethod!=="auto"){return a.translationMethod}else{return"csstransform"}}};(function(a){Ext.browser=new Ext.env.Browser(a,true);Ext.userAgent=a.toLowerCase();Ext.SSL_SECURE_URL=Ext.isSecure&&Ext.isIE?"javascript:''":"about:blank"}(Ext.global.navigator.userAgent));Ext.env.OS=function(o,b,l){var k=this,j=Ext.Boot.osNames,d=Ext.Boot.osPrefixes,a,h="",f=k.is,c,g,e,n,m;l=l||Ext.browser;for(c in d){if(d.hasOwnProperty(c)){g=d[c];e=o.match(new RegExp("(?:"+g+")([^\\s;]+)"));if(e){a=j[c];m=e[1];if(m&&m==="HTC_"){h=new Ext.Version("2.3")}else{if(m&&m==="Silk/"){h=new Ext.Version("2.3")}else{h=new Ext.Version(e[e.length-1])}}break}}}if(!a){a=j[(o.toLowerCase().match(/mac|win|linux/)||["other"])[0]];h=new Ext.Version("")}this.name=a;this.version=h;if(b){this.setFlag(b.replace(/ simulator$/i,""))}this.setFlag(a);if(h){this.setFlag(a+(h.getMajor()||""));this.setFlag(a+h.getShortVersion())}for(c in j){if(j.hasOwnProperty(c)){n=j[c];if(!f.hasOwnProperty(a)){this.setFlag(n,(a===n))}}}if(this.name==="iOS"&&window.screen.height===568){this.setFlag("iPhone5")}if(l.is.Safari||l.is.Silk){if(this.is.Android2||this.is.Android3||l.version.shortVersion===501){l.setFlag("AndroidStock")}if(this.is.Android4){l.setFlag("AndroidStock");l.setFlag("AndroidStock4")}}};Ext.env.OS.prototype={constructor:Ext.env.OS,is:function(a){return !!this[a]},name:null,version:null,setFlag:function(a,b){if(b===undefined){b=true}if(this.flags){this.flags[a]=b}this.is[a]=b;this.is[a.toLowerCase()]=b;return this}};(function(){var a=Ext.global.navigator,g=a.userAgent,f=Ext.env.OS,e=(Ext.is||(Ext.is={})),h,d,b;f.prototype.flags=e;Ext.os=h=new f(g,a.platform);d=h.name;Ext["is"+d]=true;Ext.isMac=e.Mac=e.MacOS;var i=window.location.search.match(/deviceType=(Tablet|Phone)/),c=window.deviceType;if(i&&i[1]){b=i[1]}else{if(c==="iPhone"){b="Phone"}else{if(c==="iPad"){b="Tablet"}else{if(!h.is.Android&&!h.is.iOS&&!h.is.WindowsPhone&&/Windows|Linux|MacOS/.test(d)){b="Desktop";Ext.browser.is.WebView=!!Ext.browser.is.Ripple}else{if(h.is.iPad||h.is.RIMTablet||h.is.Android3||Ext.browser.is.Silk||(h.is.Android&&g.search(/mobile/i)===-1)){b="Tablet"}else{b="Phone"}}}}}h.setFlag(b,true);h.deviceType=b;delete f.prototype.flags}());Ext.feature={has:function(a){return !!this.has[a]},testElements:{},getTestElement:function(a,b){if(a===undefined){a="div"}else{if(typeof a!=="string"){return a}}if(b){return document.createElement(a)}if(!this.testElements[a]){this.testElements[a]=document.createElement(a)}return this.testElements[a]},isStyleSupported:function(c,b){var d=this.getTestElement(b).style,a=Ext.String.capitalize(c);if(typeof d[c]!=="undefined"||typeof d[Ext.browser.getStylePrefix(c)+a]!=="undefined"){return true}return false},isStyleSupportedWithoutPrefix:function(b,a){var c=this.getTestElement(a).style;if(typeof c[b]!=="undefined"){return true}return false},isEventSupported:function(c,a){if(a===undefined){a=window}var e=this.getTestElement(a),b="on"+c.toLowerCase(),d=(b in e);if(!d){if(e.setAttribute&&e.removeAttribute){e.setAttribute(b,"");d=typeof e[b]==="function";if(typeof e[b]!=="undefined"){e[b]=undefined}e.removeAttribute(b)}}return d},getStyle:function(c,b){var a=c.ownerDocument.defaultView,d=(a?a.getComputedStyle(c,null):c.currentStyle);return(d||c.style)[b]},getSupportedPropertyName:function(b,a){var c=Ext.browser.getVendorProperyName(a);if(c in b){return c}else{if(a in b){return a}}return null},detect:function(i){var j=this,l=document,f=j.toRun||j.tests,e=f.length,b=l.createElement("div"),c=[],o=Ext.supports,m=j.has,a,h,g,d,k;b.innerHTML='
    ';if(i){l.body.appendChild(b)}d=j.preDetected[Ext.browser.identity]||[];while(e--){g=f[e];k=d[e];a=g.name;h=g.names;if(k===undefined){if(!i&&g.ready){c.push(g);continue}k=g.fn.call(j,l,b)}if(a){o[a]=m[a]=k}else{if(h){while(h.length){a=h.pop();o[a]=m[a]=k}}}}if(i){l.body.removeChild(b)}j.toRun=c},report:function(){var b=[],a=this.tests.length,c;for(c=0;c
    ";a=(b.childNodes.length===1);b.innerHTML="";return a}},{name:"touchScroll",fn:function(){var a=0;if(Ext.os.is.Desktop&&(navigator.maxTouchPoints||navigator.msMaxTouchPoints)){a=1}else{if(Ext.supports.Touch){a=2}}return a}},{name:"Touch",fn:function(){var a=navigator.msMaxTouchPoints||navigator.maxTouchPoints;if(Ext.browser.is.Chrome&&Ext.browser.version.isLessThanOrEqual(39)){return(Ext.supports.TouchEvents&&a!==1)||a>1}else{return Ext.supports.TouchEvents||a>0}}},{name:"TouchEvents",fn:function(){return this.isEventSupported("touchend")}},{name:"PointerEvents",fn:function(){return navigator.pointerEnabled}},{name:"MSPointerEvents",fn:function(){return navigator.msPointerEnabled}},{name:"Orientation",fn:function(){return("orientation" in window)&&this.isEventSupported("orientationchange")}},{name:"OrientationChange",fn:function(){return this.isEventSupported("orientationchange")}},{name:"DeviceMotion",fn:function(){return this.isEventSupported("devicemotion")}},{names:["Geolocation","GeoLocation"],fn:function(){return"geolocation" in window.navigator}},{name:"SqlDatabase",fn:function(){return"openDatabase" in window}},{name:"WebSockets",fn:function(){return"WebSocket" in window}},{name:"Range",fn:function(){return !!document.createRange}},{name:"CreateContextualFragment",fn:function(){var a=!!document.createRange?document.createRange():false;return a&&!!a.createContextualFragment}},{name:"History",fn:function(){return("history" in window&&"pushState" in window.history)}},{name:"Css3dTransforms",fn:function(){return this.has("CssTransforms")&&this.isStyleSupported("perspective")}},{name:"CssTransforms",fn:function(){return this.isStyleSupported("transform")}},{name:"CssTransformNoPrefix",fn:function(){return this.isStyleSupportedWithoutPrefix("transform")}},{name:"CssAnimations",fn:function(){return this.isStyleSupported("animationName")}},{names:["CssTransitions","Transitions"],fn:function(){return this.isStyleSupported("transitionProperty")}},{names:["Audio","AudioTag"],fn:function(){return !!this.getTestElement("audio").canPlayType}},{name:"Video",fn:function(){return !!this.getTestElement("video").canPlayType}},{name:"LocalStorage",fn:function(){try{if("localStorage" in window&&window.localStorage!==null){localStorage.setItem("sencha-localstorage-test","test success");localStorage.removeItem("sencha-localstorage-test");return true}}catch(a){}return false}},{name:"XHR2",fn:function(){return window.ProgressEvent&&window.FormData&&window.XMLHttpRequest&&("withCredentials" in new XMLHttpRequest())}},{name:"XHRUploadProgress",fn:function(){if(window.XMLHttpRequest&&!Ext.browser.is.AndroidStock){var a=new XMLHttpRequest();return a&&("upload" in a)&&("onprogress" in a.upload)}return false}},{name:"NumericInputPlaceHolder",fn:function(){return !(Ext.browser.is.AndroidStock4&&Ext.os.version.getMinor()<2)}},{name:"matchesSelector",fn:function(){var b=document.documentElement,e="matches",d="webkitMatchesSelector",a="msMatchesSelector",c="mozMatchesSelector";return b[e]?e:b[d]?d:b[a]?a:b[c]?c:null}},{name:"RightMargin",ready:true,fn:function(b,c){var a=b.defaultView;return !(a&&a.getComputedStyle(c.firstChild.firstChild,null).marginRight!=="0px")}},{name:"DisplayChangeInputSelectionBug",fn:function(){var a=Ext.webKitVersion;return 0a";b=a.firstChild;a.innerHTML="
    b
    ";return b.innerHTML!=="a"}},{name:"IncludePaddingInWidthCalculation",ready:true,fn:function(a,b){return b.childNodes[1].firstChild.offsetWidth===210}},{name:"IncludePaddingInHeightCalculation",ready:true,fn:function(a,b){return b.childNodes[1].firstChild.offsetHeight===210}},{name:"TextAreaMaxLength",fn:function(a){return("maxlength" in a.createElement("textarea"))}},{name:"GetPositionPercentage",ready:true,fn:function(a,b){return Ext.feature.getStyle(b.childNodes[2],"left")==="10%"}},{name:"PercentageHeightOverflowBug",ready:true,fn:function(d){var a=false,c,b;if(Ext.getScrollbarSize().height){b=this.getTestElement();c=b.style;c.height="50px";c.width="50px";c.overflow="auto";c.position="absolute";b.innerHTML=['
    ','
    ',"
    "].join("");d.body.appendChild(b);if(b.firstChild.offsetHeight===50){a=true}d.body.removeChild(b)}return a}},{name:"xOriginBug",ready:true,fn:function(d,e){e.innerHTML='
    ';var c=document.getElementById("b1").getBoundingClientRect(),b=document.getElementById("b2").getBoundingClientRect(),a=document.getElementById("b3").getBoundingClientRect();return(b.left!==c.left&&a.right!==c.right)}},{name:"ScrollWidthInlinePaddingBug",ready:true,fn:function(d){var a=false,c,b;b=d.createElement("div");c=b.style;c.height="50px";c.width="50px";c.padding="10px";c.overflow="hidden";c.position="absolute";b.innerHTML='';d.body.appendChild(b);if(b.scrollWidth===70){a=true}d.body.removeChild(b);return a}},{name:"rtlVertScrollbarOnRight",ready:true,fn:function(c,d){d.innerHTML='
    ';var b=d.firstChild,a=b.firstChild;return(a.offsetLeft+a.offsetWidth!==b.offsetLeft+b.offsetWidth)}},{name:"rtlVertScrollbarOverflowBug",ready:true,fn:function(b,c){c.innerHTML='
    ';var a=c.firstChild;return a.clientHeight===a.offsetHeight}},{identity:"defineProperty",fn:function(){if(Ext.isIE8m){Ext.Object.defineProperty=Ext.emptyFn;return false}return true}},{identify:"nativeXhr",fn:function(){if(typeof XMLHttpRequest!=="undefined"){return true}XMLHttpRequest=function(){try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(a){return null}};return false}},{name:"SpecialKeyDownRepeat",fn:function(){return Ext.isWebKit?parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1],10)>=525:!((Ext.isGecko&&!Ext.isWindows)||(Ext.isOpera&&Ext.operaVersion<12))}},{name:"EmulatedMouseOver",fn:function(){return Ext.os.is.iOS}},{name:"Hashchange",fn:function(){var a=document.documentMode;return"onhashchange" in window&&(a===undefined||a>7)}},{name:"FixedTableWidthBug",ready:true,fn:function(){if(Ext.isIE8){return false}var b=document.createElement("div"),a=document.createElement("div"),c;b.setAttribute("style","display:table;table-layout:fixed;");a.setAttribute("style","display:table-cell;min-width:50px;");b.appendChild(a);document.body.appendChild(b);b.offsetWidth;b.style.width="25px";c=b.offsetWidth;document.body.removeChild(b);return c===50}},{name:"FocusinFocusoutEvents",fn:function(){return !Ext.isGecko}},{name:"AsyncFocusEvents",fn:function(){return Ext.asyncFocus=!!Ext.isIE}},{name:"accessibility",ready:true,fn:function(g){var a=g.body,h,d,e,b,c;function f(o){var j=[],l=0,n,k;if(o.indexOf("rgb(")!==-1){j=o.replace("rgb(","").replace(")","").split(", ")}else{if(o.indexOf("#")!==-1){n=o.length===7?/^#(\S\S)(\S\S)(\S\S)$/:/^#(\S)(\S)(\S)$/;k=o.match(n);if(k){j=["0x"+k[1],"0x"+k[2],"0x"+k[3]]}}}for(var m=0;m0};Ext.supports.HighContrastMode=!b.BackgroundImages;a.removeChild(h);h=d=null;return b}},0]};Ext.feature.tests.pop();Ext.supports={};Ext.feature.detect();Ext.env.Ready={blocks:(location.search||"").indexOf("ext-pauseReadyFire")>0?1:0,bound:0,delay:1,events:[],firing:false,generation:0,listeners:[],nextId:0,sortGeneration:0,state:0,timer:null,bind:function(){var a=Ext.env.Ready,b=document;if(!a.bound){if(b.readyState==="complete"){a.onReadyEvent({type:b.readyState||"body"})}else{a.bound=1;if(Ext.browser.is.PhoneGap&&!Ext.os.is.Desktop){a.bound=2;b.addEventListener("deviceready",a.onReadyEvent,false)}b.addEventListener("DOMContentLoaded",a.onReadyEvent,false);window.addEventListener("load",a.onReadyEvent,false)}}},block:function(){++this.blocks;Ext.isReady=false},fireReady:function(){var a=Ext.env.Ready;if(!a.state){Ext._readyTime=Ext.ticks();Ext.isDomReady=true;a.state=1;Ext.feature.detect(true);if(!a.delay){a.handleReady()}else{if(navigator.standalone){a.timer=Ext.defer(function(){a.timer=null;a.handleReadySoon()},1)}else{a.handleReadySoon()}}}},handleReady:function(){var a=this;if(a.state===1){a.state=2;Ext._beforeReadyTime=Ext.ticks();a.invokeAll();Ext._afterReadyTime=Ext.ticks()}},handleReadySoon:function(a){var b=this;if(!b.timer){b.timer=Ext.defer(function(){b.timer=null;b.handleReady()},a||b.delay)}},invoke:function(b){var a=b.delay;if(a){Ext.defer(b.fn,a,b.scope)}else{if(Ext.elevateFunction){Ext.elevateFunction(b.fn,b.scope)}else{b.fn.call(b.scope)}}},invokeAll:function(){if(Ext.elevateFunction){Ext.elevateFunction(this.doInvokeAll,this)}else{this.doInvokeAll()}},doInvokeAll:function(){var b=this,a=b.listeners,c;if(!b.blocks){Ext.isReady=true}b.firing=true;while(a.length){if(b.sortGeneration!==b.generation){b.sortGeneration=b.generation;a.sort(b.sortFn)}c=a.pop();if(b.blocks&&!c.dom){a.push(c);break}b.invoke(c)}b.firing=false},makeListener:function(d,c,b){var a={fn:d,id:++this.nextId,scope:c,dom:false,priority:0};if(b){Ext.apply(a,b)}a.phase=a.dom?0:1;return a},on:function(c,b,a){var d=Ext.env.Ready,e=d.makeListener(c,b,a);if(d.state===2&&!d.firing&&(e.dom||!d.blocks)){d.invoke(e)}else{d.listeners.push(e);++d.generation;if(!d.bound){d.bind()}}},onReadyEvent:function(b){var a=Ext.env.Ready;if(Ext.elevateFunction){Ext.elevateFunction(a.doReadyEvent,a,arguments)}else{a.doReadyEvent(b)}},doReadyEvent:function(b){var a=this;if(b&&b.type){a.events.push(b)}if(a.bound>0){a.unbind();a.bound=-1}if(!a.state){a.fireReady()}},sortFn:function(d,c){return -((d.phase-c.phase)||(c.priority-d.priority)||(d.id-c.id))},unblock:function(){var a=this;if(a.blocks){if(!--a.blocks){if(a.state===2&&!a.firing){a.invokeAll()}}}},unbind:function(){var a=this,b=document;if(a.bound>1){b.removeEventListener("deviceready",a.onReadyEvent,false)}b.removeEventListener("DOMContentLoaded",a.onReadyEvent,false);window.removeEventListener("load",a.onReadyEvent,false)}};(function(){var a=Ext.env.Ready;if(Ext.isIE9m){Ext.apply(a,{scrollTimer:null,readyStatesRe:/complete/i,pollScroll:function(){var b=true;try{document.documentElement.doScroll("left")}catch(c){b=false}if(b&&document.body){a.onReadyEvent({type:"doScroll"})}else{a.scrollTimer=Ext.defer(a.pollScroll,20)}return b},bind:function(){if(a.bound){return}var d=document,b;try{b=window.frameElement===undefined}catch(c){}if(!b||!d.documentElement.doScroll){a.pollScroll=Ext.emptyFn}else{if(a.pollScroll()){return}}if(d.readyState==="complete"){a.onReadyEvent({type:"already "+(d.readyState||"body")})}else{d.attachEvent("onreadystatechange",a.onReadyStateChange);window.attachEvent("onload",a.onReadyEvent);a.bound=1}},unbind:function(){document.detachEvent("onreadystatechange",a.onReadyStateChange);window.detachEvent("onload",a.onReadyEvent);if(Ext.isNumber(a.scrollTimer)){clearTimeout(a.scrollTimer);a.scrollTimer=null}},onReadyStateChange:function(){var b=document.readyState;if(a.readyStatesRe.test(b)){a.onReadyEvent({type:b})}}})}Ext.onDocumentReady=function(e,d,b){var c={dom:true};if(b){Ext.apply(c,b)}a.on(e,d,c)};Ext.onReady=function(d,c,b){a.on(d,c,b)};Ext.onInternalReady=function(d,c,b){a.on(d,c,Ext.apply({priority:1000},b))};a.bind()}());Ext.Loader=(new function(){var c=this,a=Ext.ClassManager,g=Ext.Boot,d=Ext.Class,i=Ext.env.Ready,h=Ext.Function.alias,f=["extend","mixins","requires"],m={},j=[],b=[],e=[],n={},l={},k={enabled:true,scriptChainDelay:false,disableCaching:true,disableCachingParam:"_dc",paths:a.paths,preserveScripts:true,scriptCharset:undefined},o={disableCaching:true,disableCachingParam:true,preserveScripts:true,scriptChainDelay:"loadDelay"};Ext.apply(c,{isInHistory:m,isLoading:false,history:j,config:k,readyListeners:b,optionalRequires:e,requiresMap:n,hasFileLoadError:false,scriptsLoading:0,classesLoading:[],syncModeEnabled:false,missingQueue:l,init:function(){var u=document.getElementsByTagName("script"),p=u[u.length-1].src,z=p.substring(0,p.lastIndexOf("/")+1),x=Ext._classPathMetadata,y=Ext.Microloader,r=Ext.manifest,s,v,w,t,q;if(p.indexOf("packages/core/src/")!==-1){z=z+"../../"}else{if(p.indexOf("/core/src/class/")!==-1){z=z+"../../../"}}if(!a.getPath("Ext")){a.setPath("Ext",z+"src")}if(x){Ext._classPathMetadata=null;c.addClassPathMappings(x)}if(r){s=r.loadOrder;v=Ext.Boot.baseUrl;if(s&&r.bootRelative){for(w=s.length,t=0;t1)?"es":"")+": "+s.join(", "))}if(w.length){c.loadScripts({url:w,_classNames:s})}else{c.checkReady()}}else{if(x){x.call(y)}c.checkReady()}if(c.syncModeEnabled){if(r===1){return a.get(p[0])}}return c},makeLoadCallback:function(p,q){return function(){var s=[],r=p.length;while(r-->0){s[r]=a.get(p[r])}return q.apply(this,s)}},onLoadFailure:function(){var p=this,q=p.onError;c.hasFileLoadError=true;--c.scriptsLoading;if(q){q.call(p.userScope,p)}else{Ext.log.error("[Ext.Loader] Some requested files failed to load.")}c.checkReady()},onLoadSuccess:function(){var p=this,q=p.onLoad;--c.scriptsLoading;if(q){q.call(p.userScope,p)}c.checkReady()},reportMissingClasses:function(){if(!c.syncModeEnabled&&!c.scriptsLoading&&c.isLoading&&!c.hasFileLoadError){var p=[],r=[];for(var q in l){p.push(q);r.push(l[q])}if(p.length){throw new Error("The following classes are not declared even if their files have been loaded: '"+p.join("', '")+"'. Please check the source code of their corresponding files for possible typos: '"+r.join("', '"))}}},onReady:function(r,q,t,p){if(t){i.on(r,q,p)}else{var s=i.makeListener(r,q,p);if(c.isLoading){b.push(s)}else{i.invoke(s)}}},addUsedClasses:function(r){var p,q,s;if(r){r=(typeof r==="string")?[r]:r;for(q=0,s=r.length;q ")+" -> "+v[0])}for(C=0,w=n[H].length;C0){c.loadScripts({url:t,sequential:true})}}}if(v.uses){t=v.uses;c.addUsedClasses(t)}});a.onCreated(c.historyPush);c.init()}());Ext._endTime=Ext.ticks();if(Ext._beforereadyhandler){Ext._beforereadyhandler()}Ext.define("Ext.overrides.util.Positionable",{override:"Ext.util.Positionable",anchorTo:function(g,e,b,a,i,j){var f=this,h=!Ext.isEmpty(i),c=function(){f.alignTo(g,e,b,a);Ext.callback(j,f)},d=f.getAnchor();f.removeAnchor();Ext.apply(d,{fn:c,scroll:h});Ext.on("resize",c,null);if(h){Ext.getWin().on("scroll",c,null,{buffer:!isNaN(i)?i:50})}c();return f},getAnchor:function(){var b=this.el,c,a;if(!b.dom){return}c=b.getData();a=c._anchor;if(!a){a=c._anchor={}}return a},removeAnchor:function(){var a=this.getAnchor();if(a&&a.fn){Ext.un("resize",a.fn);if(a.scroll){Ext.getWin().on("scroll",a.fn)}delete a.fn}return this},setBox:function(c,a){var b=this;if(c.isRegion){c={x:c.left,y:c.top,width:c.right-c.left,height:c.bottom-c.top}}if(a){b.constrainBox(c);b.animate(Ext.applyIf({to:c,listeners:{afteranimate:Ext.Function.bind(b.afterSetPosition,b,[c.x,c.y])}},a))}else{b.callParent([c])}return b}});Ext.define("Ext.overrides.event.Event",{override:"Ext.event.Event",mousedownEvents:{mousedown:1,pointerdown:1,touchstart:1},injectEvent:(function(){var d,e={},c;if(!Ext.isIE9m&&document.createEvent){d={createHtmlEvent:function(j,h,g,f){var i=j.createEvent("HTMLEvents");i.initEvent(h,g,f);return i},createMouseEvent:function(t,r,l,k,n,j,h,i,f,q,p,m,o){var g=t.createEvent("MouseEvents"),s=t.defaultView||window;if(g.initMouseEvent){g.initMouseEvent(r,l,k,s,n,j,h,j,h,i,f,q,p,m,o)}else{g=t.createEvent("UIEvents");g.initEvent(r,l,k);g.view=s;g.detail=n;g.screenX=j;g.screenY=h;g.clientX=j;g.clientY=h;g.ctrlKey=i;g.altKey=f;g.metaKey=p;g.shiftKey=q;g.button=m;g.relatedTarget=o}return g},createUIEvent:function(l,j,h,g,i){var k=l.createEvent("UIEvents"),f=l.defaultView||window;k.initUIEvent(j,h,g,f,i);return k},fireEvent:function(h,f,g){h.dispatchEvent(g)}}}else{if(document.createEventObject){c={0:1,1:4,2:2};d={createHtmlEvent:function(j,h,g,f){var i=j.createEventObject();i.bubbles=g;i.cancelable=f;return i},createMouseEvent:function(s,r,l,k,n,j,h,i,f,q,p,m,o){var g=s.createEventObject();g.bubbles=l;g.cancelable=k;g.detail=n;g.screenX=j;g.screenY=h;g.clientX=j;g.clientY=h;g.ctrlKey=i;g.altKey=f;g.shiftKey=q;g.metaKey=p;g.button=c[m]||m;g.relatedTarget=o;return g},createUIEvent:function(k,i,g,f,h){var j=k.createEventObject();j.bubbles=g;j.cancelable=f;return j},fireEvent:function(h,f,g){h.fireEvent("on"+f,g)}}}}Ext.Object.each({load:[false,false],unload:[false,false],select:[true,false],change:[true,false],submit:[true,true],reset:[true,false],resize:[true,false],scroll:[true,false]},function(h,i){var g=i[0],f=i[1];e[h]=function(l,j){var k=d.createHtmlEvent(h,g,f);d.fireEvent(l,h,k)}});function b(h,g){var f=(h!=="mousemove");return function(l,i){var k=i.getXY(),j=d.createMouseEvent(l.ownerDocument,h,true,f,g,k[0],k[1],i.ctrlKey,i.altKey,i.shiftKey,i.metaKey,i.button,i.relatedTarget);d.fireEvent(l,h,j)}}Ext.each(["click","dblclick","mousedown","mouseup","mouseover","mousemove","mouseout"],function(f){e[f]=b(f,1)});Ext.Object.each({focusin:[true,false],focusout:[true,false],activate:[true,true],focus:[false,false],blur:[false,false]},function(h,i){var g=i[0],f=i[1];e[h]=function(l,j){var k=d.createUIEvent(l.ownerDocument,h,g,f,1);d.fireEvent(l,h,k)}});if(!d){e={};d={}}function a(g,f){}return function(i){var h=this,g=e[h.type]||a,f=i?(i.dom||i):h.getTarget();g(f,h)}}()),preventDefault:function(){var d=this,c=d.browserEvent,b=d.parentEvent,a,e;if(typeof c.type!=="unknown"){d.defaultPrevented=true;if(b){b.defaultPrevented=true}if(c.preventDefault){c.preventDefault()}else{if(c.type==="mousedown"){e=c.target;a=e.getAttribute("unselectable");if(a!=="on"){e.setAttribute("unselectable","on");Ext.defer(function(){e.setAttribute("unselectable",a)},1)}}c.returnValue=false;if(c.ctrlKey||c.keyCode>111&&c.keyCode<124){c.keyCode=-1}}}return d},stopPropagation:function(){var b=this,a=b.browserEvent;if(typeof a.type!=="unknown"){if(b.mousedownEvents[b.type]){Ext.GlobalEvents.fireMouseDown(b)}b.callParent()}return b},deprecated:{"5.0":{methods:{clone:function(){return new this.self(this.browserEvent,this)}}}}},function(){var a=this,d,c=function(f){if(f.keyCode===9){a.forwardTab=!f.shiftKey}},b=function(f){if(f.keyCode===9){delete a.forwardTab}};if(Ext.isIE9m){d={0:0,1:0,4:1,2:2};a.override({statics:{enableIEAsync:function(g){var e,f={};for(e in g){f[e]=g[e]}return f}},constructor:function(h,i,f,e){var g=this;g.callParent([h,i,f,e]);g.button=d[h.button];if(h.type==="contextmenu"){g.button=2}g.toElement=h.toElement;g.fromElement=h.fromElement},mouseLeaveRe:/(mouseout|mouseleave)/,mouseEnterRe:/(mouseover|mouseenter)/,enableIEAsync:function(e){this.browserEvent=this.self.enableIEAsync(e)},getRelatedTarget:function(f,j,e){var h=this,g,i;if(!h.relatedTarget){g=h.type;if(h.mouseLeaveRe.test(g)){i=h.toElement}else{if(h.mouseEnterRe.test(g)){i=h.fromElement}}if(i){h.relatedTarget=h.self.resolveTextNode(i)}}return h.callParent([f,j,e])}});document.attachEvent("onkeydown",c);document.attachEvent("onkeyup",b);window.attachEvent("onunload",function(){document.detachEvent("onkeydown",c);document.detachEvent("onkeyup",b)})}else{if(document.addEventListener){document.addEventListener("keydown",c,true);document.addEventListener("keyup",b,true)}}});Ext.define("Ext.overrides.event.publisher.Dom",{override:"Ext.event.publisher.Dom"},function(f){if(Ext.isIE9m){var b=document.documentElement,e=document.body,d=f.prototype,a,c;d.target=document;d.directBoundListeners={};a=function(i,h,g){i.target=i.srcElement||window;i.currentTarget=this;if(g){h.onDirectCaptureEvent(i)}else{h.onDirectEvent(i)}};c=function(h,g){h.target=h.srcElement||window;h.currentTarget=this;g.onDirectCaptureEvent(h)};f.override({addDelegatedListener:function(g){this.delegatedListeners[g]=1;this.target.attachEvent("on"+g,this.onDelegatedEvent)},removeDelegatedListener:function(g){delete this.delegatedListeners[g];this.target.detachEvent("on"+g,this.onDelegatedEvent)},addDirectListener:function(j,k,i){var m=this,n=k.dom,l=Ext.Function.bind(a,n,[m,i],true),g=m.directBoundListeners,h=g[j]||(g[j]={});h[n.id]=l;if(n.attachEvent){n.attachEvent("on"+j,l)}else{m.callParent(arguments)}},removeDirectListener:function(g,h){var i=h.dom;if(i.detachEvent){i.detachEvent("on"+g,this.directBoundListeners[g][i.id])}else{this.callParent(arguments)}},doDelegatedEvent:function(h,g){h.target=h.srcElement||window;if(h.type==="focusin"){h.relatedTarget=h.fromElement===e||h.fromElement===b?null:h.fromElement}else{if(h.type==="focusout"){h.relatedTarget=h.toElement===e||h.toElement===b?null:h.toElement}}return this.callParent([h,g])}});Ext.apply(d.directEvents,d.captureEvents);Ext.apply(d.directEvents,{change:1,input:1,paste:1});d.captureEvents={}}});Ext.define("Ext.overrides.event.publisher.Gesture",{override:"Ext.event.publisher.Gesture"},function(){if(Ext.isIE9m){this.override({updateTouches:function(c,a){var d=c.browserEvent,b=c.getXY();d.pageX=b[0];d.pageY=b[1];this.callParent([c,a])},doDelegatedEvent:function(a){this.callParent([Ext.event.Event.enableIEAsync(a)])}})}});Ext.define("Ext.overrides.dom.Element",(function(){var i,k=window,D=document,L="hidden",p="isClipped",J="overflow",t="overflow-x",s="overflow-y",w="originalClip",B="height",e="width",f="visibility",a="display",F="none",N="offsets",c="clip",l="originalDisplay",G="visibilityMode",x="isVisible",j=Ext.baseCSSPrefix+"hidden-offsets",o=Ext.baseCSSPrefix+"hidden-clip",O=['",'",'"].join(""),I=/(?:]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,C=/(?:)((\n|\r|.)*?)(?:<\/script>)/ig,b=/\ssrc=([\'\"])(.*?)\1/i,n=/\S/,h=/\stype=([\'\"])(.*?)\1/i,z=/^-ms-/,A=/(-[a-z])/gi,H=function(P,Q){return Q.charAt(1).toUpperCase()},m=Ext.baseCSSPrefix+"masked",y=Ext.baseCSSPrefix+"masked-relative",u=Ext.baseCSSPrefix+"mask-msg",r=/^body/i,q={},v=function(Q){var R=Q.getData(),P=R[G];if(P===undefined){R[G]=P=i.VISIBILITY}return P},M=D.createRange?D.createRange():null,E={INPUT:true,TEXTAREA:true};if(Ext.isIE8){var g=D.createElement("div"),K=[],d=Ext.Function.createBuffered(function(){var P=K.length,Q;for(Q=0;Q"+Ext.String.format(O,P)+""));Q.selectNode("."+P+"-mc").appendChild(this.dom);return Q},clean:function(Q){var S=this,U=S.dom,T=S.getData(),V=U.firstChild,R=-1,P;if(T.isCleaned&&Q!==true){return S}while(V){P=V.nextSibling;if(V.nodeType===3){if(!(n.test(V.nodeValue))){U.removeChild(V)}else{if(P&&P.nodeType===3){V.appendData(Ext.String.trim(P.data));U.removeChild(P);P=V.nextSibling;V.nodeIndex=++R}}}else{Ext.fly(V,"_clean").clean();V.nodeIndex=++R}V=P}T.isCleaned=true;return S},empty:M?function(){var P=this.dom;if(P.firstChild){M.setStartBefore(P.firstChild);M.setEndAfter(P.lastChild);M.deleteContents()}}:function(){var P=this.dom;while(P.lastChild){P.removeChild(P.lastChild)}},clearListeners:function(){this.removeAnchor();this.callParent()},clearPositioning:function(P){P=P||"";return this.setStyle({left:P,right:P,top:P,bottom:P,"z-index":"",position:"static"})},createProxy:function(P,T,S){P=(typeof P==="object")?P:{tag:"div",role:"presentation",cls:P};var R=this,Q=T?Ext.DomHelper.append(T,P,true):Ext.DomHelper.insertBefore(R.dom,P,true);Q.setVisibilityMode(i.DISPLAY);Q.hide();if(S&&R.setBox&&R.getBox){Q.setBox(R.getBox())}return Q},clearOpacity:function(){return this.setOpacity("")},clip:function(){var Q=this,R=Q.getData(),P;if(!R[p]){R[p]=true;P=Q.getStyle([J,t,s]);R[w]={o:P[J],x:P[t],y:P[s]};Q.setStyle(J,L);Q.setStyle(t,L);Q.setStyle(s,L)}return Q},destroy:function(){var R=this,T=R.dom,S=R.getData(),Q,P;if(T&&R.isAnimate){R.stopAnimation()}R.callParent();if(T&&Ext.isIE8&&(T.window!=T)&&(T.nodeType!==9)&&(T.tagName!=="BODY")&&(T.tagName!=="HTML")){K[K.length]=T;d()}if(S){Q=S.maskEl;P=S.maskMsg;if(Q){Q.destroy()}if(P){P.destroy()}}},enableDisplayMode:function(Q){var P=this;P.setVisibilityMode(i.DISPLAY);if(Q!==undefined){P.getData()[l]=Q}return P},fadeIn:function(R){var P=this,Q=P.dom;P.animate(Ext.apply({},R,{opacity:1,internalListeners:{beforeanimate:function(T){var S=Ext.fly(Q,"_anim");if(S.isStyle("display","none")){S.setDisplayed("")}else{S.show()}}}}));return this},fadeOut:function(R){var P=this,Q=P.dom;R=Ext.apply({opacity:0,internalListeners:{afteranimate:function(T){if(Q&&T.to.opacity===0){var S=Ext.fly(Q,"_anim");if(R.useDisplay){S.setDisplayed(false)}else{S.hide()}}}}},R);P.animate(R);return P},fixDisplay:function(){var P=this;if(P.isStyle(a,F)){P.setStyle(f,L);P.setStyle(a,P._getDisplay());if(P.isStyle(a,F)){P.setStyle(a,"block")}}},frame:function(P,S,T){var R=this,U=R.dom,Q;P=P||"#C3DAF9";S=S||1;T=T||{};Q=function(){var Y=Ext.fly(U,"_anim"),X=this,Z,W,V;Y.show();Z=Y.getBox();W=Ext.getBody().createChild({role:"presentation",id:Y.dom.id+"-anim-proxy",style:{position:"absolute","pointer-events":"none","z-index":35000,border:"0px solid "+P}});V=new Ext.fx.Anim({target:W,duration:T.duration||1000,iterations:S,from:{top:Z.y,left:Z.x,borderWidth:0,opacity:1,height:Z.height,width:Z.width},to:{top:Z.y-20,left:Z.x-20,borderWidth:10,opacity:0,height:Z.height+40,width:Z.width+40}});V.on("afteranimate",function(){W.destroy();X.end()})};R.animate({duration:(Math.max(T.duration,500)*2)||2000,listeners:{beforeanimate:{fn:Q}},callback:T.callback,scope:T.scope});return R},getColor:function(Q,R,W){var T=this.getStyle(Q),S=W||W===""?W:"#",V,P,U=0;if(!T||(/transparent|inherit/.test(T))){return R}if(/^r/.test(T)){T=T.slice(4,T.length-1).split(",");P=T.length;for(;U5?S.toLowerCase():R)},getLoader:function(){var Q=this,R=Q.getData(),P=R.loader;if(!P){R.loader=P=new Ext.ElementLoader({target:Q})}return P},getPositioning:function(Q){var P=this.getStyle(["left","top","position","z-index"]),R=this.dom;if(Q){if(P.left==="auto"){P.left=R.offsetLeft+"px"}if(P.top==="auto"){P.top=R.offsetTop+"px"}}return P},ghost:function(P,S){var R=this,T=R.dom,Q;P=P||"b";Q=function(){var X=Ext.fly(T,"_anim"),W=X.getWidth(),V=X.getHeight(),Y=X.getXY(),U=X.getPositioning(),Z={opacity:0};switch(P){case"t":Z.y=Y[1]-V;break;case"l":Z.x=Y[0]-W;break;case"r":Z.x=Y[0]+W;break;case"b":Z.y=Y[1]+V;break;case"tl":Z.x=Y[0]-W;Z.y=Y[1]-V;break;case"bl":Z.x=Y[0]-W;Z.y=Y[1]+V;break;case"br":Z.x=Y[0]+W;Z.y=Y[1]+V;break;case"tr":Z.x=Y[0]+W;Z.y=Y[1]-V;break}this.to=Z;this.on("afteranimate",function(){var aa=Ext.fly(T,"_anim");if(aa){aa.hide();aa.clearOpacity();aa.setPositioning(U)}})};R.animate(Ext.applyIf(S||{},{duration:500,easing:"ease-out",listeners:{beforeanimate:Q}}));return R},hide:function(P){if(typeof P==="string"){this.setVisible(false,P);return this}this.setVisible(false,this.anim(P));return this},highlight:function(S,Q){var W=this,T=W.dom,Y={},V,Z,U,R,P,X;Q=Q||{};R=Q.listeners||{};U=Q.attr||"backgroundColor";Y[U]=S||"ffff9c";if(!Q.to){Z={};Z[U]=Q.endColor||W.getColor(U,"ffffff","")}else{Z=Q.to}Q.listeners=Ext.apply(Ext.apply({},R),{beforeanimate:function(){V=T.style[U];var aa=Ext.fly(T,"_anim");aa.clearOpacity();aa.show();P=R.beforeanimate;if(P){X=P.fn||P;return X.apply(P.scope||R.scope||k,arguments)}},afteranimate:function(){if(T){T.style[U]=V}P=R.afteranimate;if(P){X=P.fn||P;X.apply(P.scope||R.scope||k,arguments)}}});W.animate(Ext.apply({},Q,{duration:1000,easing:"ease-in",from:Y,to:Z}));return W},hover:function(Q,P,S,R){var T=this;T.on("mouseenter",Q,S||T.dom,R);T.on("mouseleave",P,S||T.dom,R);return T},initDD:function(R,Q,S){var P=new Ext.dd.DD(Ext.id(this.dom),R,Q);return Ext.apply(P,S)},initDDProxy:function(R,Q,S){var P=new Ext.dd.DDProxy(Ext.id(this.dom),R,Q);return Ext.apply(P,S)},initDDTarget:function(R,Q,S){var P=new Ext.dd.DDTarget(Ext.id(this.dom),R,Q);return Ext.apply(P,S)},isFocusable:function(){var Q=this.dom,P=false,R;if(Q&&!Q.disabled){R=Q.nodeName;P=!!Ext.Element.naturallyFocusableTags[R]||((R==="A"||R==="LINK")&&!!Q.href)||Q.getAttribute("tabIndex")!=null||Q.contentEditable==="true";if(Ext.isIE8&&R==="INPUT"&&Q.type==="hidden"){P=false}P=P&&this.isVisible(true)}return P},isInputField:function(){var Q=this.dom,P=Q.contentEditable;if((E[Q.tagName]&&Q.type!=="button")||(P===""||P==="true")){return true}return false},isTabbable:function(R){var S=this.dom,U=false,T,Q,P;if(S&&!S.disabled){T=S.nodeName;P=S.getAttribute("tabIndex");Q=P!=null;P-=0;if(T==="A"||T==="LINK"){if(S.href){U=Q&&P<0?false:true}else{if(S.contentEditable==="true"){U=!Q||(Q&&P>=0)?true:false}else{U=Q&&P>=0?true:false}}}else{if(S.contentEditable==="true"||Ext.Element.naturallyTabbableTags[T]){U=Q&&P<0?false:true}else{if(Q&&P>=0){U=true}}}if(Ext.isIE8&&T==="INPUT"&&S.type==="hidden"){U=false}U=U&&(R||((!this.component||this.component.isVisible(true))&&this.isVisible(true)))}return U},isMasked:function(P){var T=this,V=T.getData(),S=V.maskEl,Q=V.maskMsg,U=false,R;if(S&&S.isVisible()){if(Q){Q.center(T)}U=true}else{if(P){R=T.findParentNode();if(R){return Ext.fly(R).isMasked(P)}}}return U},load:function(P){this.getLoader().load(P);return this},mask:function(W,U,P){var S=this,V=S.dom,T=S.getData(),R=T.maskEl,Q;if(!(r.test(V.tagName)&&S.getStyle("position")==="static")){S.addCls(y)}if(R){R.destroy()}R=Ext.DomHelper.append(V,{role:"presentation",cls:Ext.baseCSSPrefix+"mask "+Ext.baseCSSPrefix+"border-box",children:{role:"presentation",cls:U?u+" "+U:u,cn:{tag:"div",role:"presentation",cls:Ext.baseCSSPrefix+"mask-msg-inner",cn:{tag:"div",role:"presentation",cls:Ext.baseCSSPrefix+"mask-msg-text",html:W||""}}}},true);Q=Ext.get(R.dom.firstChild);T.maskEl=R;S.addCls(m);R.setDisplayed(true);if(typeof W==="string"){Q.setDisplayed(true);Q.center(S)}else{Q.setDisplayed(false)}if(V===D.body){R.addCls(Ext.baseCSSPrefix+"mask-fixed")}S.saveTabbableState({skipSelf:V===D.body});if(Ext.isIE9m&&V!==D.body&&S.isStyle("height","auto")){R.setSize(undefined,P||S.getHeight())}return R},monitorMouseLeave:function(P,S,R){var T=this,U,Q={mouseleave:function(V){if(Ext.isIE9m){V.enableIEAsync()}U=Ext.defer(S,P,R||T,[V])},mouseenter:function(){clearTimeout(U)}};T.on(Q);return Q},puff:function(T){var S=this,U=S.dom,Q,R=S.getBox(),P=S.getStyle(["width","height","left","right","top","bottom","position","z-index","font-size","opacity"],true);T=Ext.applyIf(T||{},{easing:"ease-out",duration:500,useDisplay:false});Q=function(){var V=Ext.fly(U,"_anim");V.clearOpacity();V.show();this.to={width:R.width*2,height:R.height*2,x:R.x-(R.width/2),y:R.y-(R.height/2),opacity:0,fontSize:"200%"};this.on("afteranimate",function(){var W=Ext.fly(U,"_anim");if(W){if(T.useDisplay){W.setDisplayed(false)}else{W.hide()}W.setStyle(P);Ext.callback(T.callback,T.scope)}})};S.animate({duration:T.duration,easing:T.easing,listeners:{beforeanimate:{fn:Q}}});return S},selectable:function(){var P=this;P.dom.unselectable="";P.removeCls(i.unselectableCls);P.addCls(i.selectableCls);return P},setCapture:function(){var P=this.dom;if(Ext.isIE9m&&P.setCapture){P.setCapture()}},setHeight:function(P,Q){var R=this;if(!Q||!R.anim){R.callParent(arguments)}else{if(!Ext.isObject(Q)){Q={}}R.animate(Ext.applyIf({to:{height:P}},Q))}return R},setHorizontal:function(){var Q=this,P=Q.verticalCls;delete Q.vertical;if(P){delete Q.verticalCls;Q.removeCls(P)}delete Q.setWidth;delete Q.setHeight;if(!Ext.isIE8){delete Q.getWidth;delete Q.getHeight}delete Q.styleHooks},updateText:function(S){var P=this,R,Q;if(R){Q=R.firstChild;if(!Q||(Q.nodeType!==3||Q.nextSibling)){Q=D.createTextNode();P.empty();R.appendChild(Q)}if(S){Q.data=S}}},setHtml:function(R,Q,W,S){var T=this,V,U,P;if(!T.dom){return T}R=R||"";U=T.dom;if(Q!==true){U.innerHTML=R;Ext.callback(W,T);return T}V=Ext.id();R+='';P=Ext.interval(function(){var ad,aa,Z,Y,X,ac,ab;if(!(ac=D.getElementById(V))){return false}clearInterval(P);Ext.removeNode(ac);ad=Ext.getHead().dom;while((aa=I.exec(R))){Z=aa[1];Y=Z?Z.match(b):false;if(Y&&Y[2]){ab=D.createElement("script");ab.src=Y[2];X=Z.match(h);if(X&&X[2]){ab.type=X[2]}ad.appendChild(ab)}else{if(aa[2]&&aa[2].length>0){if(S){Ext.functionFactory(aa[2]).call(S)}else{Ext.globalEval(aa[2])}}}}Ext.callback(W,S||T)},20);U.innerHTML=R.replace(C,"");return T},setOpacity:function(Q,P){var R=this;if(!R.dom){return R}if(!P||!R.anim){R.setStyle("opacity",Q)}else{if(typeof P!="object"){P={duration:350,easing:"ease-in"}}R.animate(Ext.applyIf({to:{opacity:Q}},P))}return R},setPositioning:function(P){return this.setStyle(P)},setVertical:function(S,P){var R=this,Q=i.prototype;R.vertical=true;if(P){R.addCls(R.verticalCls=P)}R.setWidth=Q.setHeight;R.setHeight=Q.setWidth;if(!Ext.isIE8){R.getWidth=Q.getHeight;R.getHeight=Q.getWidth}R.styleHooks=(S===270)?Q.verticalStyleHooks270:Q.verticalStyleHooks90},setSize:function(R,P,Q){var S=this;if(Ext.isObject(R)){Q=P;P=R.height;R=R.width}if(!Q||!S.anim){S.dom.style.width=i.addUnits(R);S.dom.style.height=i.addUnits(P);if(S.shadow||S.shim){S.syncUnderlays()}}else{if(Q===true){Q={}}S.animate(Ext.applyIf({to:{width:R,height:P}},Q))}return S},setVisible:function(T,P){var R=this,S=R.dom,Q=v(R);if(typeof P==="string"){switch(P){case a:Q=i.DISPLAY;break;case f:Q=i.VISIBILITY;break;case N:Q=i.OFFSETS;break;case c:Q=i.CLIP;break}R.setVisibilityMode(Q);P=false}if(!P||!R.anim){if(Q===i.DISPLAY){return R.setDisplayed(T)}else{if(Q===i.OFFSETS){R[T?"removeCls":"addCls"](j)}else{if(Q===i.CLIP){R[T?"removeCls":"addCls"](o)}else{if(Q===i.VISIBILITY){R.fixDisplay();S.style.visibility=T?"":L}}}}}else{if(T){R.setOpacity(0.01);R.setVisible(true)}if(!Ext.isObject(P)){P={duration:350,easing:"ease-in"}}R.animate(Ext.applyIf({callback:function(){if(!T){Ext.fly(S).setVisible(false).setOpacity(1)}},to:{opacity:(T)?1:0}},P))}R.getData()[x]=T;if(R.shadow||R.shim){R.setUnderlaysVisible(T)}return R},setWidth:function(Q,P){var R=this;if(!P||!R.anim){R.callParent(arguments)}else{if(!Ext.isObject(P)){P={}}R.animate(Ext.applyIf({to:{width:Q}},P))}return R},setX:function(P,Q){return this.setXY([P,this.getY()],Q)},setXY:function(R,P){var Q=this;if(!P||!Q.anim){Q.callParent([R])}else{if(!Ext.isObject(P)){P={}}Q.animate(Ext.applyIf({to:{x:R[0],y:R[1]}},P))}return this},setY:function(Q,P){return this.setXY([this.getX(),Q],P)},show:function(P){if(typeof P==="string"){this.setVisible(true,P);return this}this.setVisible(true,this.anim(P));return this},slideIn:function(S,R,T){var V=this,Q=V.dom,Y=Q.style,X,P,U,W;S=S||"t";R=R||{};X=function(){var ad=this,ac=R.listeners,ab=Ext.fly(Q,"_anim"),ae,Z,af,aa;if(!T){ab.fixDisplay()}ae=ab.getBox();if((S=="t"||S=="b")&&ae.height===0){ae.height=Q.scrollHeight}else{if((S=="l"||S=="r")&&ae.width===0){ae.width=Q.scrollWidth}}Z=ab.getStyle(["width","height","left","right","top","bottom","position","z-index"],true);ab.setSize(ae.width,ae.height);if(R.preserveScroll){U=ab.cacheScrollValues()}aa=ab.wrap({role:"presentation",id:Ext.id()+"-anim-wrap-for-"+ab.dom.id,style:{visibility:T?"visible":"hidden"}});W=aa.dom.parentNode;aa.setPositioning(ab.getPositioning());if(aa.isStyle("position","static")){aa.position("relative")}ab.clearPositioning("auto");aa.clip();if(U){U()}ab.setStyle({visibility:"",position:"absolute"});if(T){aa.setSize(ae.width,ae.height)}switch(S){case"t":af={from:{width:ae.width+"px",height:"0px"},to:{width:ae.width+"px",height:ae.height+"px"}};Y.bottom="0px";break;case"l":af={from:{width:"0px",height:ae.height+"px"},to:{width:ae.width+"px",height:ae.height+"px"}};V.anchorAnimX(S);break;case"r":af={from:{x:ae.x+ae.width,width:"0px",height:ae.height+"px"},to:{x:ae.x,width:ae.width+"px",height:ae.height+"px"}};V.anchorAnimX(S);break;case"b":af={from:{y:ae.y+ae.height,width:ae.width+"px",height:"0px"},to:{y:ae.y,width:ae.width+"px",height:ae.height+"px"}};break;case"tl":af={from:{x:ae.x,y:ae.y,width:"0px",height:"0px"},to:{width:ae.width+"px",height:ae.height+"px"}};Y.bottom="0px";V.anchorAnimX("l");break;case"bl":af={from:{y:ae.y+ae.height,width:"0px",height:"0px"},to:{y:ae.y,width:ae.width+"px",height:ae.height+"px"}};V.anchorAnimX("l");break;case"br":af={from:{x:ae.x+ae.width,y:ae.y+ae.height,width:"0px",height:"0px"},to:{x:ae.x,y:ae.y,width:ae.width+"px",height:ae.height+"px"}};V.anchorAnimX("r");break;case"tr":af={from:{x:ae.x+ae.width,width:"0px",height:"0px"},to:{x:ae.x,width:ae.width+"px",height:ae.height+"px"}};Y.bottom="0px";V.anchorAnimX("r");break}aa.show();P=Ext.apply({},R);delete P.listeners;P=new Ext.fx.Anim(Ext.applyIf(P,{target:aa,duration:500,easing:"ease-out",from:T?af.to:af.from,to:T?af.from:af.to}));P.on("afteranimate",function(){var ag=Ext.fly(Q,"_anim");ag.setStyle(Z);if(T){if(R.useDisplay){ag.setDisplayed(false)}else{ag.hide()}}if(aa.dom){if(aa.dom.parentNode){aa.dom.parentNode.insertBefore(ag.dom,aa.dom)}else{W.appendChild(ag.dom)}aa.destroy()}if(U){U()}ad.end()});if(ac){P.on(ac)}};V.animate({duration:R.duration?Math.max(R.duration,500)*2:1000,listeners:{beforeanimate:X}});return V},slideOut:function(P,Q){return this.slideIn(P,Q,true)},swallowEvent:function(Q,R){var T=this,U,P,S=function(V){V.stopPropagation();if(R){V.preventDefault()}};if(Ext.isArray(Q)){P=Q.length;for(U=0;U0){V.setAttribute(W,++Q)}else{if(V.hasAttribute("tabIndex")){V.setAttribute(S,V.getAttribute("tabIndex"))}else{V.setAttribute(S,"none")}V.setAttribute("tabIndex","-1");V.setAttribute(W,"1")}}return R},restoreTabbableState:function(P,V){var T=this.dom,X=Ext.Element.tabbableSavedCounterAttribute,Y=Ext.Element.tabbableSavedValueAttribute,R=[],Z,Q,R,S,U,W;if(!T){return this}if(!V){R=Ext.Array.from(T.querySelectorAll("["+X+"]"))}if(!P){R.unshift(T)}for(U=0,W=R.length;U1){S.setAttribute(X,--Q);continue}Z=S.getAttribute(Y);if(Z==="none"){S.removeAttribute("tabIndex")}else{S.setAttribute("tabIndex",Z)}S.removeAttribute(Y);S.removeAttribute(X)}return R}},deprecated:{"4.0":{methods:{pause:function(P){var Q=this;Ext.fx.Manager.setFxDefaults(Q.id,{delay:P});return Q},scale:function(P,Q,R){this.animate(Ext.apply({},R,{width:P,height:Q}));return this},shift:function(P){this.animate(P);return this}}},"4.2":{methods:{moveTo:function(P,R,Q){return this.setXY([P,R],Q)},setBounds:function(Q,T,S,P,R){return this.setBox({x:Q,y:T,width:S,height:P},R)},setLeftTop:function(S,R){var Q=this,P=Q.dom.style;P.left=i.addUnits(S);P.top=i.addUnits(R);if(Q.shadow||Q.shim){Q.syncUnderlays()}return Q},setLocation:function(P,R,Q){return this.setXY([P,R],Q)}}},"5.0":{methods:{getAttributeNS:function(Q,P){return this.getAttribute(P,Q)},getCenterXY:function(){return this.getAlignToXY(D,"c-c")},getComputedHeight:function(){return Math.max(this.dom.offsetHeight,this.dom.clientHeight)||parseFloat(this.getStyle(B))||0},getComputedWidth:function(){return Math.max(this.dom.offsetWidth,this.dom.clientWidth)||parseFloat(this.getStyle(e))||0},getStyleSize:function(){var T=this,U=this.dom,Q=(U===D||U===D.body),S,P,R;if(Q){return{width:i.getViewportWidth(),height:i.getViewportHeight()}}S=T.getStyle(["height","width"],true);if(S.width&&S.width!=="auto"){P=parseFloat(S.width)}if(S.height&&S.height!=="auto"){R=parseFloat(S.height)}return{width:P||T.getWidth(true),height:R||T.getHeight(true)}},isBorderBox:function(){return true},isDisplayed:function(){return !this.isStyle("display","none")},focusable:"isFocusable"}}}}})(),function(){var p=Ext.dom.Element,o=p.prototype,v=!Ext.isIE8,b=document,l=b.defaultView,u=/alpha\(opacity=(.*)\)/i,g=/^\s+|\s+$/g,w=o.styleHooks,s=Ext.supports,e,n,d,r,f,x,c;o._init(p);delete o._init;Ext.plainTableCls=Ext.baseCSSPrefix+"table-plain";Ext.plainListCls=Ext.baseCSSPrefix+"list-plain";if(Ext.CompositeElementLite){Ext.CompositeElementLite.importElementMethods()}if(!s.Opacity&&Ext.isIE){Ext.apply(w.opacity,{get:function(A){var z=A.style.filter,y,k;if(z.match){y=z.match(u);if(y){k=parseFloat(y[1]);if(!isNaN(k)){return k?k/100:0}}}return 1},set:function(A,y){var k=A.style,z=k.filter.replace(u,"").replace(g,"");k.zoom=1;if(typeof(y)==="number"&&y>=0&&y<1){y*=100;k.filter=z+(z.length?" ":"")+"alpha(opacity="+y+")"}else{k.filter=z}}})}if(!s.matchesSelector){var i=/^([a-z]+|\*)?(?:\.([a-z][a-z\-_0-9]*))?$/i,j=/\-/g,a,t=function(k,y){var z=new RegExp("(?:^|\\s+)"+y.replace(j,"\\-")+"(?:\\s+|$)");if(k&&k!=="*"){k=k.toUpperCase();return function(A){return A.tagName===k&&z.test(A.className)}}return function(A){return z.test(A.className)}},q=function(k){k=k.toUpperCase();return function(y){return y.tagName===k}},m={};o.matcherCache=m;o.is=function(k){if(!k){return true}var y=this.dom,E,A,D,C,B,z,F;if(y.nodeType!==1){return false}if(!(D=Ext.isFunction(k)?k:m[k])){if(!(A=k.match(i))){C=y.parentNode;if(!C){B=true;C=a||(a=b.createDocumentFragment());a.appendChild(y)}z=Ext.Array.indexOf(Ext.fly(C,"_is").query(k),y)!==-1;if(B){a.removeChild(y)}return z}F=A[1];E=A[2];m[k]=D=E?t(F,E):q(F)}return D(y)}}if(!l||!l.getComputedStyle){o.getStyle=function(L,G){var H=this,C=H.dom,N=typeof L!=="string",z=L,I=z,F=1,A=G,y=H.styleHooks,M,E,K,J,B,k,D;if(N){K={};z=I[0];D=0;if(!(F=I.length)){return K}}if(!C||C.documentElement){return K||""}E=C.style;if(G){k=E}else{k=C.currentStyle;if(!k){A=true;k=E}}do{J=y[z];if(!J){y[z]=J={name:p.normalize(z)}}if(J.get){B=J.get(C,H,A,k)}else{M=J.name;B=k[M]}if(!N){return B}K[z]=B;z=I[++D]}while(D=9)){o.getAttribute=function(k,z){var A=this.dom,y;if(z){y=typeof A[z+":"+k];if(y!=="undefined"&&y!=="unknown"){return A[z+":"+k]||null}return null}if(k==="for"){k="htmlFor"}return A[k]||null}}Ext.onInternalReady(function(){var A=/^(?:transparent|(?:rgba[(](?:\s*\d+\s*[,]){3}\s*0\s*[)]))$/i,y=[],F=o.setWidth,G=o.setHeight,K=o.setSize,L=/^\d+(?:\.\d*)?px$/i,E,C,k,J;if(s.FixedTableWidthBug){w.width={name:"width",set:function(R,Q,O){var N=R.style,M=O._needsTableWidthFix,P=N.display;if(M){N.display="none"}N.width=Q;if(M){R.scrollWidth;N.display=P}}};o.setWidth=function(P,N){var R=this,S=R.dom,O=S.style,M=R._needsTableWidthFix,Q=O.display;if(M&&!N){O.display="none"}F.call(R,P,N);if(M&&!N){S.scrollWidth;O.display=Q}return R};o.setSize=function(Q,N,O){var S=this,T=S.dom,P=T.style,M=S._needsTableWidthFix,R=P.display;if(M&&!O){P.display="none"}K.call(S,Q,N,O);if(M&&!O){T.scrollWidth;P.display=R}return S}}if(Ext.isIE8){w.height={name:"height",set:function(R,Q,O){var N=O.component,P,M;if(N&&N._syncFrameHeight&&O===N.el){M=N.frameBody.dom.style;if(L.test(Q)){P=N.getFrameInfo();if(P){M.height=(parseInt(Q,10)-P.height)+"px"}}else{if(!Q||Q==="auto"){M.height=""}}}R.style.height=Q}};o.setHeight=function(M,O){var P=this.component,Q,N;if(P&&P._syncFrameHeight&&this===P.el){N=P.frameBody.dom.style;if(!M||M==="auto"){N.height=""}else{Q=P.getFrameInfo();if(Q){N.height=(M-Q.height)+"px"}}}return G.call(this,M,O)};o.setSize=function(Q,M,O){var P=this.component,R,N;if(P&&P._syncFrameHeight&&this===P.el){N=P.frameBody.dom.style;if(!M||M==="auto"){N.height=""}else{R=P.getFrameInfo();if(R){N.height=(M-R.height)+"px"}}}return K.call(this,Q,M,O)}}Ext.getDoc().on("selectstart",function(Q,R){var P=p.selectableCls,O=p.unselectableCls,M=R&&R.tagName;M=M&&M.toLowerCase();if(M==="input"||M==="textarea"){return}while(R&&R.nodeType===1&&R!==b.documentElement){var N=Ext.fly(R);if(N.hasCls(P)){return}if(N.hasCls(O)){Q.stopEvent();return}R=R.parentNode}});function D(Q,N,P,M){var O=M[this.name]||"";return A.test(O)?"transparent":O}function I(N,O,M){return function(){N.selectionStart=O;N.selectionEnd=M}}function H(Q){var O=s.DisplayChangeInputSelectionBug,P=s.DisplayChangeTextAreaSelectionBug,R,M,S,N;if(O||P){R=p.getActiveElement();M=R&&R.tagName;if((P&&M==="TEXTAREA")||(O&&M==="INPUT"&&R.type==="text")){if(Ext.fly(Q).isAncestor(R)){S=R.selectionStart;N=R.selectionEnd;if(Ext.isNumber(S)&&Ext.isNumber(N)){return I(R,S,N)}}}}return Ext.emptyFn}function B(S,P,R,O){var M=O.marginRight,N,Q;if(M!=="0px"){N=S.style;Q=N.display;N.display="inline-block";M=(R?O:S.ownerDocument.defaultView.getComputedStyle(S,null)).marginRight;N.display=Q}return M}function z(T,Q,S,P){var M=P.marginRight,O,N,R;if(M!=="0px"){O=T.style;N=H(T);R=O.display;O.display="inline-block";M=(S?P:T.ownerDocument.defaultView.getComputedStyle(T,"")).marginRight;O.display=R;N()}return M}if(!s.RightMargin){w.marginRight=w["margin-right"]={name:"marginRight",get:(s.DisplayChangeInputSelectionBug||s.DisplayChangeTextAreaSelectionBug)?z:B}}if(!s.TransparentColor){E=["background-color","border-color","color","outline-color"];for(C=E.length;C--;){k=E[C];J=p.normalize(k);w[k]=w[J]={name:J,get:D}}}o.verticalStyleHooks90=e=Ext.Object.chain(w);o.verticalStyleHooks270=n=Ext.Object.chain(w);e.width=w.height||{name:"height"};e.height=w.width||{name:"width"};e["margin-top"]={name:"marginLeft"};e["margin-right"]={name:"marginTop"};e["margin-bottom"]={name:"marginRight"};e["margin-left"]={name:"marginBottom"};e["padding-top"]={name:"paddingLeft"};e["padding-right"]={name:"paddingTop"};e["padding-bottom"]={name:"paddingRight"};e["padding-left"]={name:"paddingBottom"};e["border-top"]={name:"borderLeft"};e["border-right"]={name:"borderTop"};e["border-bottom"]={name:"borderRight"};e["border-left"]={name:"borderBottom"};n.width=w.height||{name:"height"};n.height=w.width||{name:"width"};n["margin-top"]={name:"marginRight"};n["margin-right"]={name:"marginBottom"};n["margin-bottom"]={name:"marginLeft"};n["margin-left"]={name:"marginTop"};n["padding-top"]={name:"paddingRight"};n["padding-right"]={name:"paddingBottom"};n["padding-bottom"]={name:"paddingLeft"};n["padding-left"]={name:"paddingTop"};n["border-top"]={name:"borderRight"};n["border-right"]={name:"borderBottom"};n["border-bottom"]={name:"borderLeft"};n["border-left"]={name:"borderTop"};if(!Ext.scopeCss){y.push(Ext.baseCSSPrefix+"body")}if(s.Touch){y.push(Ext.baseCSSPrefix+"touch")}if(Ext.isIE&&Ext.isIE9m){y.push(Ext.baseCSSPrefix+"ie",Ext.baseCSSPrefix+"ie9m");y.push(Ext.baseCSSPrefix+"ie8p");if(Ext.isIE8){y.push(Ext.baseCSSPrefix+"ie8")}else{y.push(Ext.baseCSSPrefix+"ie9",Ext.baseCSSPrefix+"ie9p")}if(Ext.isIE8m){y.push(Ext.baseCSSPrefix+"ie8m")}}if(Ext.isIE10){y.push(Ext.baseCSSPrefix+"ie10")}if(Ext.isIE10p){y.push(Ext.baseCSSPrefix+"ie10p")}if(Ext.isIE11){y.push(Ext.baseCSSPrefix+"ie11")}if(Ext.isGecko){y.push(Ext.baseCSSPrefix+"gecko")}if(Ext.isOpera){y.push(Ext.baseCSSPrefix+"opera")}if(Ext.isOpera12m){y.push(Ext.baseCSSPrefix+"opera12m")}if(Ext.isWebKit){y.push(Ext.baseCSSPrefix+"webkit")}if(Ext.isSafari){y.push(Ext.baseCSSPrefix+"safari")}if(Ext.isChrome){y.push(Ext.baseCSSPrefix+"chrome")}if(Ext.isMac){y.push(Ext.baseCSSPrefix+"mac")}if(Ext.isLinux){y.push(Ext.baseCSSPrefix+"linux")}if(!s.CSS3BorderRadius){y.push(Ext.baseCSSPrefix+"nbr")}if(!s.CSS3LinearGradient){y.push(Ext.baseCSSPrefix+"nlg")}if(s.Touch){y.push(Ext.baseCSSPrefix+"touch")}Ext.getBody().addCls(y)},null,{priority:1500})});Ext.define("Ext.overrides.GlobalEvents",{override:"Ext.GlobalEvents",deprecated:{5:{methods:{addListener:function(d,g,h,i,c,b,e){var a,f;if(d==="ready"){f=g}else{if(typeof d!=="string"){for(a in d){if(a==="ready"){f=d[a]}}}}if(f){Ext.log.warn("Ext.on('ready', fn) is deprecated. Please use Ext.onReady(fn) instead.");Ext.onReady(f)}this.callParent([d,g,h,i,c,b,e])}}}}});Ext.define("Ext.overrides.Widget",{override:"Ext.Widget",uses:["Ext.Component"],$configStrict:false,isComponent:true,liquidLayout:true,rendered:true,rendering:true,config:{renderTo:null},cachedConfig:{baseCls:Ext.baseCSSPrefix+"widget"},constructor:function(a){var b=this,c;b.callParent([a]);b.getComponentLayout();c=b.getRenderTo();if(c){b.render(c)}},addClsWithUI:function(a){this.el.addCls(a)},afterComponentLayout:Ext.emptyFn,updateLayout:function(){var a=this.getRefOwner();if(a){a.updateLayout()}},destroy:function(){var b=this,a=b.ownerCt;if(a&&a.remove){a.remove(b,false)}b.callParent()},finishRender:function(){this.rendering=false;this.initBindable()},getAnimationProps:function(){return{}},getComponentLayout:function(){var b=this,a=b.componentLayout;if(!a){a=b.componentLayout=new Ext.layout.component.Auto();a.setOwner(b)}return a},getEl:function(){return this.element},getTdCls:function(){return Ext.baseCSSPrefix+this.getTdType()+"-"+(this.ui||"default")+"-cell"},getTdType:function(){return this.xtype},getItemId:function(){return this.itemId||this.id},getSizeModel:function(){return Ext.Component.prototype.getSizeModel.apply(this,arguments)},onAdded:function(b,e,a){var d=this,c=d.inheritedState;d.ownerCt=b;d.onInheritedAdd(d,a)},onRemoved:function(b){var a=this;if(!b){a.removeBindings()}a.onInheritedRemove(b);a.ownerCt=a.ownerLayout=null},parseBox:function(a){return Ext.Element.parseBox(a)},removeClsWithUI:function(a){this.el.removeCls(a)},render:function(b,a){var e=this,c=e.element,d=Ext.Component.prototype,f;if(!e.ownerCt||e.floating){if(Ext.scopeCss){c.addCls(d.rootCls)}c.addCls(d.borderBoxCls)}if(a){f=b.childNodes[a];if(f){Ext.fly(b).insertBefore(c,f);return}}Ext.fly(b).appendChild(c)},setPosition:function(a,b){this.el.setLocalXY(a,b)},up:function(){return Ext.Component.prototype.up.apply(this,arguments)},isAncestor:function(){return Ext.Component.prototype.isAncestor.apply(this,arguments)},onFocusEnter:function(){return Ext.Component.prototype.onFocusEnter.apply(this,arguments)},onFocusLeave:function(){return Ext.Component.prototype.onFocusLeave.apply(this,arguments)},isLayoutChild:function(b){var a=this.ownerCt;return a?(a===b||a.isLayoutChild(b)):false}},function(b){var a=b.prototype;if(Ext.isIE9m){a.addElementReferenceOnDemand=a.addElementReference}});Ext.define("Ext.overrides.Progress",{override:"Ext.Progress",config:{ui:"default"}});Ext.define("Ext.overrides.app.domain.Component",{override:"Ext.app.domain.Component",requires:["Ext.Component"]},function(a){a.monitor(Ext.Component)});Ext.application=function(a){var b=function(c){Ext.onReady(function(){var d=Ext.viewport;d=d&&d.Viewport;if(d&&d.setup){d.setup(c.prototype.config.viewport)}Ext.app.Application.instance=new c()})};if(typeof a==="string"){Ext.require(a,function(){b(Ext.ClassManager.get(a))})}else{a=Ext.apply({extend:"Ext.app.Application"},a);Ext.app.setupPaths(a.name,a.appFolder,a.paths);a["paths processed"]=true;Ext.define(a.name+".$application",a,function(){b(this)})}};Ext.define("Ext.overrides.app.Application",{override:"Ext.app.Application",uses:["Ext.tip.QuickTipManager"],autoCreateViewport:false,config:{enableQuickTips:true},applyMainView:function(e){var b,d,c,a;if(typeof e==="string"){b=this.getView(e)}else{b=Ext.ClassManager.getByConfig(e)}d=b.prototype;if(!d.isViewport){a=d.plugins;a=["viewport"].concat(a?Ext.Array.from(a,true):[]);c={plugins:a}}return b.create(c)},getDependencies:function(a,f,d){var g=Ext.app.Controller,e=a.prototype,c=f.$namespace,b=f.autoCreateViewport;if(b){if(!c){Ext.raise("[Ext.app.Application] Can't resolve namespace for "+f.$className+", did you forget to specify 'name' property?")}if(b===true){b="Viewport"}else{d.push("Ext.plugin.Viewport")}g.processDependencies(e,d,c,"view",b)}},onBeforeLaunch:function(){var b=this,a=b.autoCreateViewport;if(b.getEnableQuickTips()){b.initQuickTips()}if(a){b.initViewport()}this.callParent(arguments)},getViewportName:function(){var a=null,b=this.autoCreateViewport;if(b){a=(b===true)?"Viewport":b}return a},initViewport:function(){this.setMainView(this.getViewportName())},initQuickTips:function(){Ext.tip.QuickTipManager.init()}});Ext.define("Ext.overrides.dom.Helper",(function(){var c=/^(?:table|thead|tbody|tr|td)$/i,g=/td|tr|tbody|thead/i,f="",h="
    ",b=f+"",e=""+h,a=b+"",d=""+e;return{override:"Ext.dom.Helper",ieInsertHtml:function(i,k,j){var l=null;if(Ext.isIE9m&&c.test(k.tagName)){l=this.insertIntoTable(k.tagName.toLowerCase(),i,k,j)}return l},ieOverwrite:function(j,i){if(Ext.isIE9m&&c.test(j.tagName)){while(j.firstChild){j.removeChild(j.firstChild)}if(i){return this.insertHtml("afterbegin",j,i)}}},ieTable:function(p,k,q,o){var l=-1,n=this.detachedDiv,m,j;n.innerHTML=[k,q,o].join("");while(++l - - - - -

    Login Form

    - -
    -
    - - - - - - - -
    - -
    - - Forgot password? -
    -
    - - - - diff --git a/src/webapp/websocket.html b/src/webapp/websocket.html index de4e054..2227fa8 100644 --- a/src/webapp/websocket.html +++ b/src/webapp/websocket.html @@ -2,7 +2,7 @@ WebSocket Test